From e8c41f4780123de38f437b58a32c6edc3c4b6ea8 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 5 Feb 2025 09:18:57 -0500 Subject: curl: Update script to get curl 8.12.0 --- Utilities/Scripts/update-curl.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index ffae7b6..82be5a3 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash @@ -8,7 +8,7 @@ readonly name="curl" readonly ownership="Curl Upstream " readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-8_11_1" +readonly tag="curl-8_12_0" readonly shortlog=false readonly paths=" CMake/* -- cgit v0.12 From 48b13baebc7f99442c3b872f202efd2fb92d6d7f Mon Sep 17 00:00:00 2001 From: Curl Upstream Date: Wed, 5 Feb 2025 07:42:54 +0100 Subject: curl 2025-02-05 (34cf9d54) Code extracted from: https://github.com/curl/curl.git at commit 34cf9d54a46598c44938aa7598820484d7af7133 (curl-8_12_0). --- CMake/CurlSymbolHiding.cmake | 6 +- CMake/CurlTests.c | 59 +- CMake/FindBrotli.cmake | 70 +- CMake/FindCares.cmake | 94 +-- CMake/FindGSS.cmake | 10 +- CMake/FindLDAP.cmake | 111 ++++ CMake/FindLibgsasl.cmake | 5 +- CMake/FindLibidn2.cmake | 5 +- CMake/FindLibpsl.cmake | 72 ++- CMake/FindLibrtmp.cmake | 102 +++ CMake/FindLibssh.cmake | 21 +- CMake/FindLibssh2.cmake | 72 ++- CMake/FindLibuv.cmake | 5 +- CMake/FindMSH3.cmake | 7 +- CMake/FindMbedTLS.cmake | 9 +- CMake/FindNGHTTP2.cmake | 72 ++- CMake/FindNGHTTP3.cmake | 72 ++- CMake/FindNGTCP2.cmake | 118 ++-- CMake/FindNettle.cmake | 5 +- CMake/FindQuiche.cmake | 5 +- CMake/FindRustls.cmake | 65 +- CMake/FindWolfSSL.cmake | 15 +- CMake/FindZstd.cmake | 92 +-- CMake/Macros.cmake | 30 +- CMake/OtherTests.cmake | 22 +- CMake/PickyWarnings.cmake | 74 ++- CMake/Platforms/WindowsCache.cmake | 194 ------ CMake/Utilities.cmake | 32 +- CMake/curl-config.cmake.in | 35 +- CMake/win32-cache.cmake | 205 ++++++ CMakeLists.txt | 912 +++++++++++++++----------- COPYING | 2 +- include/curl/curl.h | 52 +- include/curl/curlver.h | 8 +- include/curl/easy.h | 2 +- include/curl/system.h | 136 ++-- lib/CMakeLists.txt | 16 +- lib/Makefile.inc | 12 +- lib/altsvc.c | 145 +++-- lib/altsvc.h | 7 - lib/amigaos.c | 5 +- lib/asyn-ares.c | 258 +++++--- lib/asyn-thread.c | 110 ++-- lib/asyn.h | 64 ++ lib/bufq.c | 23 - lib/bufq.h | 8 - lib/c-hyper.c | 1254 ------------------------------------ lib/c-hyper.h | 63 -- lib/cf-h1-proxy.c | 340 +--------- lib/cf-h2-proxy.c | 2 + lib/cf-https-connect.c | 349 ++++++---- lib/cf-https-connect.h | 4 +- lib/cf-socket.c | 57 +- lib/cf-socket.h | 6 +- lib/cfilters.c | 38 +- lib/cfilters.h | 29 +- lib/conncache.c | 110 +++- lib/conncache.h | 12 +- lib/connect.c | 26 +- lib/connect.h | 2 + lib/content_encoding.c | 350 ++-------- lib/cookie.c | 96 ++- lib/cookie.h | 10 +- lib/curl_addrinfo.c | 8 - lib/curl_config.h.cmake | 43 +- lib/curl_ctype.h | 2 +- lib/curl_get_line.c | 2 + lib/curl_get_line.h | 6 + lib/curl_gethostname.c | 4 + lib/curl_gssapi.c | 9 + lib/curl_hmac.h | 6 +- lib/curl_multibyte.c | 233 ++++++- lib/curl_rtmp.c | 6 + lib/curl_setup.h | 196 +++--- lib/curl_setup_once.h | 26 +- lib/curl_sha256.h | 2 +- lib/curl_sha512_256.c | 77 +-- lib/curl_sspi.h | 12 +- lib/curl_trc.c | 72 ++- lib/curl_trc.h | 125 ++-- lib/dict.c | 1 + lib/doh.c | 192 ++---- lib/doh.h | 14 +- lib/dynbuf.c | 12 + lib/dynbuf.h | 5 + lib/easy.c | 58 +- lib/easy_lock.h | 6 +- lib/easyoptions.c | 2 +- lib/file.c | 38 +- lib/fopen.c | 2 +- lib/ftp.c | 23 +- lib/functypes.h | 1 + lib/getinfo.c | 13 + lib/gopher.c | 2 + lib/hash.c | 2 + lib/hmac.c | 5 +- lib/hostip.c | 22 +- lib/hostip.h | 38 +- lib/hsts.c | 65 +- lib/hsts.h | 2 +- lib/http.c | 631 +++++++++++++----- lib/http.h | 66 +- lib/http2.c | 84 +-- lib/http2.h | 16 +- lib/http_aws_sigv4.c | 152 +++-- lib/http_negotiate.c | 3 +- lib/http_ntlm.c | 6 + lib/http_proxy.c | 28 +- lib/httpsrr.c | 167 +++++ lib/httpsrr.h | 76 +++ lib/imap.c | 4 +- lib/inet_ntop.c | 6 +- lib/inet_ntop.h | 9 +- lib/inet_pton.c | 38 +- lib/inet_pton.h | 4 + lib/krb5.c | 9 + lib/ldap.c | 23 +- lib/llist.c | 64 +- lib/llist.h | 4 + lib/macos.c | 2 +- lib/memdebug.c | 4 +- lib/mime.c | 10 +- lib/mprintf.c | 39 +- lib/mqtt.c | 1 + lib/multi.c | 368 ++--------- lib/multihandle.h | 6 +- lib/multiif.h | 3 +- lib/netrc.c | 26 +- lib/nonblock.c | 12 +- lib/noproxy.c | 2 +- lib/openldap.c | 4 +- lib/parsedate.c | 6 +- lib/pingpong.c | 2 +- lib/pop3.c | 5 + lib/progress.c | 14 +- lib/request.c | 19 +- lib/request.h | 13 +- lib/rtsp.c | 10 +- lib/rtsp.h | 3 - lib/select.c | 42 +- lib/select.h | 9 +- lib/sendf.c | 19 - lib/sendf.h | 3 - lib/setopt.c | 31 +- lib/setup-vms.h | 2 +- lib/setup-win32.h | 11 - lib/sha256.c | 2 +- lib/share.c | 27 +- lib/share.h | 11 +- lib/smb.c | 6 +- lib/smtp.c | 4 +- lib/socketpair.c | 2 +- lib/socks_gssapi.c | 9 + lib/strcase.c | 4 +- lib/strerror.c | 4 +- lib/strparse.c | 136 ++++ lib/strparse.h | 71 ++ lib/strtoofft.c | 2 +- lib/telnet.c | 9 +- lib/tftp.c | 3 +- lib/transfer.c | 57 +- lib/transfer.h | 9 - lib/url.c | 42 +- lib/urlapi.c | 162 ++--- lib/urldata.h | 157 ++--- lib/vauth/digest_sspi.c | 2 +- lib/vauth/krb5_gssapi.c | 9 + lib/vauth/krb5_sspi.c | 2 +- lib/vauth/ntlm_sspi.c | 2 +- lib/vauth/spnego_gssapi.c | 9 + lib/vauth/spnego_sspi.c | 2 +- lib/vauth/vauth.c | 12 +- lib/version.c | 39 +- lib/vquic/curl_msh3.c | 25 +- lib/vquic/curl_ngtcp2.c | 280 +++++--- lib/vquic/curl_osslq.c | 92 ++- lib/vquic/curl_quiche.c | 15 +- lib/vquic/vquic-tls.c | 34 +- lib/vquic/vquic-tls.h | 15 +- lib/vquic/vquic.c | 30 +- lib/vquic/vquic.h | 8 - lib/vssh/libssh.c | 2 + lib/vssh/libssh2.c | 2 + lib/vssh/wolfssh.c | 2 + lib/vtls/bearssl.c | 56 +- lib/vtls/gtls.c | 303 +++++---- lib/vtls/gtls.h | 27 +- lib/vtls/mbedtls.c | 212 +++--- lib/vtls/openssl.c | 496 +++++++++----- lib/vtls/openssl.h | 26 +- lib/vtls/rustls.c | 19 +- lib/vtls/schannel.c | 57 +- lib/vtls/schannel.h | 2 +- lib/vtls/sectransp.c | 60 +- lib/vtls/vtls.c | 597 +++-------------- lib/vtls/vtls.h | 79 +-- lib/vtls/vtls_int.h | 68 +- lib/vtls/vtls_scache.c | 1170 +++++++++++++++++++++++++++++++++ lib/vtls/vtls_scache.h | 218 +++++++ lib/vtls/vtls_spack.c | 345 ++++++++++ lib/vtls/vtls_spack.h | 43 ++ lib/vtls/wolfssl.c | 178 +++-- lib/vtls/wolfssl.h | 20 +- lib/vtls/x509asn1.c | 29 +- lib/vtls/x509asn1.h | 3 + lib/warnless.c | 34 +- lib/warnless.h | 2 - lib/ws.c | 189 +++--- lib/ws.h | 11 +- 209 files changed, 8297 insertions(+), 6826 deletions(-) create mode 100644 CMake/FindLDAP.cmake create mode 100644 CMake/FindLibrtmp.cmake delete mode 100644 CMake/Platforms/WindowsCache.cmake create mode 100644 CMake/win32-cache.cmake delete mode 100644 lib/c-hyper.c delete mode 100644 lib/c-hyper.h create mode 100644 lib/httpsrr.c create mode 100644 lib/httpsrr.h create mode 100644 lib/strparse.c create mode 100644 lib/strparse.h create mode 100644 lib/vtls/vtls_scache.c create mode 100644 lib/vtls/vtls_scache.h create mode 100644 lib/vtls/vtls_spack.c create mode 100644 lib/vtls/vtls_spack.h diff --git a/CMake/CurlSymbolHiding.cmake b/CMake/CurlSymbolHiding.cmake index 16ec3fe..31a97cb 100644 --- a/CMake/CurlSymbolHiding.cmake +++ b/CMake/CurlSymbolHiding.cmake @@ -29,11 +29,13 @@ if(WIN32 AND (ENABLE_DEBUG OR ENABLE_CURLDEBUG)) # e.g. curl_easy_perform_ev() or curl_dbg_*(), # so disable symbol hiding for debug builds and for memory tracking. set(CURL_HIDDEN_SYMBOLS OFF) +elseif(DOS OR AMIGA) + set(CURL_HIDDEN_SYMBOLS OFF) endif() set(CURL_HIDES_PRIVATE_SYMBOLS FALSE) -unset(CURL_EXTERN_SYMBOL) -unset(CURL_CFLAG_SYMBOLS_HIDE) +set(CURL_EXTERN_SYMBOL "") +set(CURL_CFLAG_SYMBOLS_HIDE "") if(CURL_HIDDEN_SYMBOLS) if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT MSVC) diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 0334618..1428238 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -50,7 +50,6 @@ int main(void) int flags = 0; if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK)) return 1; - ; return 0; } #endif @@ -147,24 +146,21 @@ int main(void) { return 0; } #endif #ifdef HAVE_FILE_OFFSET_BITS -#ifdef _FILE_OFFSET_BITS #undef _FILE_OFFSET_BITS -#endif #define _FILE_OFFSET_BITS 64 #include - /* Check that off_t can represent 2**63 - 1 correctly. - We cannot simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ +/* Check that off_t can represent 2**63 - 1 correctly. + We cannot simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; +int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; int main(void) { return 0; } #endif #ifdef HAVE_IOCTLSOCKET -/* includes start */ #ifdef _WIN32 # include #endif @@ -180,10 +176,7 @@ int main(void) #endif #ifdef HAVE_IOCTLSOCKET_CAMEL -/* includes start */ -#ifdef _WIN32 -# include -#endif +#include int main(void) { /* IoctlSocket source code */ @@ -195,9 +188,9 @@ int main(void) #endif #ifdef HAVE_IOCTLSOCKET_CAMEL_FIONBIO -/* includes start */ -#ifdef _WIN32 -# include +#include +#ifdef HAVE_SYS_IOCTL_H +# include #endif int main(void) { @@ -211,7 +204,6 @@ int main(void) #endif #ifdef HAVE_IOCTLSOCKET_FIONBIO -/* includes start */ #ifdef _WIN32 # include #endif @@ -227,7 +219,6 @@ int main(void) #ifdef HAVE_IOCTL_FIONBIO /* headers for FIONBIO test */ -/* includes start */ #ifdef HAVE_SYS_TYPES_H # include #endif @@ -255,7 +246,6 @@ int main(void) #ifdef HAVE_IOCTL_SIOCGIFADDR /* headers for FIONBIO test */ -/* includes start */ #ifdef HAVE_SYS_TYPES_H # include #endif @@ -283,18 +273,15 @@ int main(void) #endif #ifdef HAVE_SETSOCKOPT_SO_NONBLOCK -/* includes start */ #ifdef _WIN32 # include #endif -/* includes start */ #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif -/* includes end */ int main(void) { if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0)) @@ -372,7 +359,6 @@ int main(void) #endif #ifdef HAVE_ATOMIC -/* includes start */ #ifdef HAVE_SYS_TYPES_H # include #endif @@ -382,8 +368,6 @@ int main(void) #ifdef HAVE_STDATOMIC_H # include #endif -/* includes end */ - int main(void) { _Atomic int i = 1; @@ -393,14 +377,12 @@ int main(void) #endif #ifdef HAVE_WIN32_WINNT -/* includes start */ #ifdef _WIN32 # ifndef NOGDI -# define NOGDI +# define NOGDI # endif # include #endif -/* includes end */ #define enquote(x) #x #define expand(x) enquote(x) @@ -411,3 +393,20 @@ int main(void) return 0; } #endif + +#ifdef MINGW64_VERSION +#ifdef __MINGW32__ +# include <_mingw.h> +#endif + +#define enquote(x) #x +#define expand(x) enquote(x) +#pragma message("MINGW64_VERSION=" \ + expand(__MINGW64_VERSION_MAJOR) "." \ + expand(__MINGW64_VERSION_MINOR)) + +int main(void) +{ + return 0; +} +#endif diff --git a/CMake/FindBrotli.cmake b/CMake/FindBrotli.cmake index 767abf0..8a692bd 100644 --- a/CMake/FindBrotli.cmake +++ b/CMake/FindBrotli.cmake @@ -34,47 +34,47 @@ # - `BROTLI_FOUND`: System has brotli. # - `BROTLI_INCLUDE_DIRS`: The brotli include directories. # - `BROTLI_LIBRARIES`: The brotli library names. +# - `BROTLI_LIBRARY_DIRS`: The brotli library directories. +# - `BROTLI_PC_REQUIRES`: The brotli pkg-config packages. +# - `BROTLI_CFLAGS`: Required compiler flags. # - `BROTLI_VERSION`: Version of brotli. -if(CURL_USE_PKGCONFIG) +set(BROTLI_PC_REQUIRES "libbrotlidec") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED BROTLI_INCLUDE_DIR AND + NOT DEFINED BROTLICOMMON_LIBRARY AND + NOT DEFINED BROTLIDEC_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_BROTLI "libbrotlidec") + pkg_check_modules(BROTLI "libbrotlicommon") + pkg_check_modules(BROTLIDEC ${BROTLI_PC_REQUIRES}) endif() -find_path(BROTLI_INCLUDE_DIR "brotli/decode.h" - HINTS - ${PC_BROTLI_INCLUDEDIR} - ${PC_BROTLI_INCLUDE_DIRS} -) - -find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon" - HINTS - ${PC_BROTLI_LIBDIR} - ${PC_BROTLI_LIBRARY_DIRS} -) -find_library(BROTLIDEC_LIBRARY NAMES "brotlidec" - HINTS - ${PC_BROTLI_LIBDIR} - ${PC_BROTLI_LIBRARY_DIRS} -) +if(BROTLI_FOUND AND BROTLIDEC_FOUND) + list(APPEND BROTLIDEC_LIBRARIES ${BROTLI_LIBRARIES}) # order is significant: brotlidec then brotlicommon + list(REVERSE BROTLIDEC_LIBRARIES) + list(REMOVE_DUPLICATES BROTLIDEC_LIBRARIES) + list(REVERSE BROTLIDEC_LIBRARIES) + set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARIES}) + string(REPLACE ";" " " BROTLI_CFLAGS "${BROTLI_CFLAGS}") + message(STATUS "Found Brotli (via pkg-config): ${BROTLI_INCLUDE_DIRS} (found version \"${BROTLI_VERSION}\")") +else() + find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") + find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon") + find_library(BROTLIDEC_LIBRARY NAMES "brotlidec") -if(PC_BROTLI_VERSION) - set(BROTLI_VERSION ${PC_BROTLI_VERSION}) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Brotli + REQUIRED_VARS + BROTLI_INCLUDE_DIR + BROTLIDEC_LIBRARY + BROTLICOMMON_LIBRARY + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Brotli - REQUIRED_VARS - BROTLI_INCLUDE_DIR - BROTLIDEC_LIBRARY - BROTLICOMMON_LIBRARY - VERSION_VAR - BROTLI_VERSION -) + if(BROTLI_FOUND) + set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) + set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARY} ${BROTLICOMMON_LIBRARY}) + endif() -if(BROTLI_FOUND) - set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) - set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARY} ${BROTLICOMMON_LIBRARY}) + mark_as_advanced(BROTLI_INCLUDE_DIR BROTLIDEC_LIBRARY BROTLICOMMON_LIBRARY) endif() - -mark_as_advanced(BROTLI_INCLUDE_DIR BROTLIDEC_LIBRARY BROTLICOMMON_LIBRARY) diff --git a/CMake/FindCares.cmake b/CMake/FindCares.cmake index ac55be1..67dec09 100644 --- a/CMake/FindCares.cmake +++ b/CMake/FindCares.cmake @@ -33,58 +33,64 @@ # - `CARES_FOUND`: System has c-ares. # - `CARES_INCLUDE_DIRS`: The c-ares include directories. # - `CARES_LIBRARIES`: The c-ares library names. +# - `CARES_LIBRARY_DIRS`: The c-ares library directories. +# - `CARES_PC_REQUIRES`: The c-ares pkg-config packages. +# - `CARES_CFLAGS`: Required compiler flags. # - `CARES_VERSION`: Version of c-ares. -if(CURL_USE_PKGCONFIG) +set(CARES_PC_REQUIRES "libcares") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED CARES_INCLUDE_DIR AND + NOT DEFINED CARES_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_CARES "libcares") + pkg_check_modules(CARES ${CARES_PC_REQUIRES}) endif() -find_path(CARES_INCLUDE_DIR NAMES "ares.h" - HINTS - ${PC_CARES_INCLUDEDIR} - ${PC_CARES_INCLUDE_DIRS} -) +if(CARES_FOUND) + string(REPLACE ";" " " CARES_CFLAGS "${CARES_CFLAGS}") + message(STATUS "Found Cares (via pkg-config): ${CARES_INCLUDE_DIRS} (found version \"${CARES_VERSION}\")") +else() + find_path(CARES_INCLUDE_DIR NAMES "ares.h") + find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares") -find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares" - HINTS - ${PC_CARES_LIBDIR} - ${PC_CARES_LIBRARY_DIRS} -) + unset(CARES_VERSION CACHE) + if(CARES_INCLUDE_DIR AND EXISTS "${CARES_INCLUDE_DIR}/ares_version.h") + set(_version_regex1 "#[\t ]*define[\t ]+ARES_VERSION_MAJOR[\t ]+([0-9]+).*") + set(_version_regex2 "#[\t ]*define[\t ]+ARES_VERSION_MINOR[\t ]+([0-9]+).*") + set(_version_regex3 "#[\t ]*define[\t ]+ARES_VERSION_PATCH[\t ]+([0-9]+).*") + file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str1 REGEX "${_version_regex1}") + file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str2 REGEX "${_version_regex2}") + file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str3 REGEX "${_version_regex3}") + string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") + string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") + string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") + set(CARES_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") + unset(_version_regex1) + unset(_version_regex2) + unset(_version_regex3) + unset(_version_str1) + unset(_version_str2) + unset(_version_str3) + endif() -if(PC_CARES_VERSION) - set(CARES_VERSION ${PC_CARES_VERSION}) -elseif(CARES_INCLUDE_DIR AND EXISTS "${CARES_INCLUDE_DIR}/ares_version.h") - set(_version_regex1 "#[\t ]*define[\t ]+ARES_VERSION_MAJOR[\t ]+([0-9]+).*") - set(_version_regex2 "#[\t ]*define[\t ]+ARES_VERSION_MINOR[\t ]+([0-9]+).*") - set(_version_regex3 "#[\t ]*define[\t ]+ARES_VERSION_PATCH[\t ]+([0-9]+).*") - file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str1 REGEX "${_version_regex1}") - file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str2 REGEX "${_version_regex2}") - file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str3 REGEX "${_version_regex3}") - string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") - string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") - string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") - set(CARES_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") - unset(_version_regex1) - unset(_version_regex2) - unset(_version_regex3) - unset(_version_str1) - unset(_version_str2) - unset(_version_str3) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Cares + REQUIRED_VARS + CARES_INCLUDE_DIR + CARES_LIBRARY + VERSION_VAR + CARES_VERSION + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Cares - REQUIRED_VARS - CARES_INCLUDE_DIR - CARES_LIBRARY - VERSION_VAR - CARES_VERSION -) + if(CARES_FOUND) + set(CARES_INCLUDE_DIRS ${CARES_INCLUDE_DIR}) + set(CARES_LIBRARIES ${CARES_LIBRARY}) + endif() -if(CARES_FOUND) - set(CARES_INCLUDE_DIRS ${CARES_INCLUDE_DIR}) - set(CARES_LIBRARIES ${CARES_LIBRARY}) + mark_as_advanced(CARES_INCLUDE_DIR CARES_LIBRARY) endif() -mark_as_advanced(CARES_INCLUDE_DIR CARES_LIBRARY) +if(CARES_FOUND AND WIN32) + list(APPEND CARES_LIBRARIES "iphlpapi") # for if_indextoname and others +endif() diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index 94fdc5f..c1802ee 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -99,7 +99,7 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _GSS_CFLAGS "${_GSS_CFLAGS}") foreach(_flag IN LISTS _GSS_CFLAGS) - if(_flag MATCHES "^-I.*") + if(_flag MATCHES "^-I") string(REGEX REPLACE "^-I" "" _val "${_flag}") list(APPEND _GSS_INCLUDE_DIRS "${_val}") else() @@ -123,10 +123,10 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") foreach(_flag IN LISTS _gss_lib_flags) - if(_flag MATCHES "^-l.*") + if(_flag MATCHES "^-l") string(REGEX REPLACE "^-l" "" _val "${_flag}") list(APPEND _GSS_LIBRARIES "${_val}") - elseif(_flag MATCHES "^-L.*") + elseif(_flag MATCHES "^-L") string(REGEX REPLACE "^-L" "" _val "${_flag}") list(APPEND _GSS_LIBRARY_DIRS "${_val}") endif() @@ -156,7 +156,7 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr if(_gss_configure_failed) set(GSS_FLAVOUR "Heimdal") # most probably, should not really matter else() - if(_gss_vendor MATCHES ".*H|heimdal.*") + if(_gss_vendor MATCHES "H|heimdal") set(GSS_FLAVOUR "Heimdal") else() set(GSS_FLAVOUR "MIT") @@ -175,7 +175,7 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr if(_GSS_INCLUDE_DIRS) # jay, we have found something cmake_push_check_state() - set(CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIRS}") + list(APPEND CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIRS}") check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers) if(_gss_have_mit_headers) diff --git a/CMake/FindLDAP.cmake b/CMake/FindLDAP.cmake new file mode 100644 index 0000000..4f18e04 --- /dev/null +++ b/CMake/FindLDAP.cmake @@ -0,0 +1,111 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# Find the ldap library +# +# Input variables: +# +# - `LDAP_INCLUDE_DIR`: The ldap include directory. +# - `LDAP_LIBRARY`: Path to `ldap` library. +# - `LDAP_LBER_LIBRARY`: Path to `lber` library. +# +# Result variables: +# +# - `LDAP_FOUND`: System has ldap. +# - `LDAP_INCLUDE_DIRS`: The ldap include directories. +# - `LDAP_LIBRARIES`: The ldap library names. +# - `LDAP_LIBRARY_DIRS`: The ldap library directories. +# - `LDAP_PC_REQUIRES`: The ldap pkg-config packages. +# - `LDAP_CFLAGS`: Required compiler flags. +# - `LDAP_VERSION`: Version of ldap. + +set(LDAP_PC_REQUIRES "ldap") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED LDAP_INCLUDE_DIR AND + NOT DEFINED LDAP_LIBRARY AND + NOT DEFINED LDAP_LBER_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(LDAP ${LDAP_PC_REQUIRES}) + pkg_check_modules(LDAP_LBER "lber") +endif() + +if(LDAP_FOUND AND LDAP_LBER_FOUND) + list(APPEND LDAP_LIBRARIES ${LDAP_LBER_LIBRARIES}) + list(REVERSE LDAP_LIBRARIES) + list(REMOVE_DUPLICATES LDAP_LIBRARIES) + list(REVERSE LDAP_LIBRARIES) + string(REPLACE ";" " " LDAP_CFLAGS "${LDAP_CFLAGS}") + message(STATUS "Found LDAP (via pkg-config): ${LDAP_INCLUDE_DIRS} (found version \"${LDAP_VERSION}\")") +else() + set(LDAP_PC_REQUIRES "") # Depend on pkg-config only when found via pkg-config + + # On Apple the SDK LDAP gets picked up from + # 'MacOSX.sdk/System/Library/Frameworks/LDAP.framework/Headers', which contains + # ldap.h and lber.h both being stubs to include and . + # This causes an infinite inclusion loop in compile. Also do this for libraries + # to avoid picking up the 'ldap.framework' with a full path. + set(_save_cmake_system_framework_path ${CMAKE_SYSTEM_FRAMEWORK_PATH}) + set(CMAKE_SYSTEM_FRAMEWORK_PATH "") + find_path(LDAP_INCLUDE_DIR NAMES "ldap.h") + find_library(LDAP_LIBRARY NAMES "ldap") + find_library(LDAP_LBER_LIBRARY NAMES "lber") + set(CMAKE_SYSTEM_FRAMEWORK_PATH ${_save_cmake_system_framework_path}) + + unset(LDAP_VERSION CACHE) + if(LDAP_INCLUDE_DIR AND EXISTS "${LDAP_INCLUDE_DIR}/ldap_features.h") + set(_version_regex1 "#[\t ]*define[\t ]+LDAP_VENDOR_VERSION_MAJOR[\t ]+([0-9]+).*") + set(_version_regex2 "#[\t ]*define[\t ]+LDAP_VENDOR_VERSION_MINOR[\t ]+([0-9]+).*") + set(_version_regex3 "#[\t ]*define[\t ]+LDAP_VENDOR_VERSION_PATCH[\t ]+([0-9]+).*") + file(STRINGS "${LDAP_INCLUDE_DIR}/ldap_features.h" _version_str1 REGEX "${_version_regex1}") + file(STRINGS "${LDAP_INCLUDE_DIR}/ldap_features.h" _version_str2 REGEX "${_version_regex2}") + file(STRINGS "${LDAP_INCLUDE_DIR}/ldap_features.h" _version_str3 REGEX "${_version_regex3}") + string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") + string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") + string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") + set(LDAP_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") + unset(_version_regex1) + unset(_version_regex2) + unset(_version_regex3) + unset(_version_str1) + unset(_version_str2) + unset(_version_str3) + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LDAP + REQUIRED_VARS + LDAP_INCLUDE_DIR + LDAP_LIBRARY + LDAP_LBER_LIBRARY + VERSION_VAR + LDAP_VERSION + ) + + if(LDAP_FOUND) + set(LDAP_INCLUDE_DIRS ${LDAP_INCLUDE_DIR}) + set(LDAP_LIBRARIES ${LDAP_LIBRARY} ${LDAP_LBER_LIBRARY}) + endif() + + mark_as_advanced(LDAP_INCLUDE_DIR LDAP_LIBRARY LDAP_LBER_LIBRARY) +endif() diff --git a/CMake/FindLibgsasl.cmake b/CMake/FindLibgsasl.cmake index 82ed07e..c0ce673 100644 --- a/CMake/FindLibgsasl.cmake +++ b/CMake/FindLibgsasl.cmake @@ -34,14 +34,17 @@ # - `LIBGSASL_INCLUDE_DIRS`: The libgsasl include directories. # - `LIBGSASL_LIBRARIES`: The libgsasl library names. # - `LIBGSASL_LIBRARY_DIRS`: The libgsasl library directories. +# - `LIBGSASL_PC_REQUIRES`: The libgsasl pkg-config packages. # - `LIBGSASL_CFLAGS`: Required compiler flags. # - `LIBGSASL_VERSION`: Version of libgsasl. +set(LIBGSASL_PC_REQUIRES "libgsasl") + if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBGSASL_INCLUDE_DIR AND NOT DEFINED LIBGSASL_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBGSASL "libgsasl") + pkg_check_modules(LIBGSASL ${LIBGSASL_PC_REQUIRES}) endif() if(LIBGSASL_FOUND) diff --git a/CMake/FindLibidn2.cmake b/CMake/FindLibidn2.cmake index 35580ae..a8887e8 100644 --- a/CMake/FindLibidn2.cmake +++ b/CMake/FindLibidn2.cmake @@ -34,14 +34,17 @@ # - `LIBIDN2_INCLUDE_DIRS`: The libidn2 include directories. # - `LIBIDN2_LIBRARIES`: The libidn2 library names. # - `LIBIDN2_LIBRARY_DIRS`: The libidn2 library directories. +# - `LIBIDN2_PC_REQUIRES`: The libidn2 pkg-config packages. # - `LIBIDN2_CFLAGS`: Required compiler flags. # - `LIBIDN2_VERSION`: Version of libidn2. +set(LIBIDN2_PC_REQUIRES "libidn2") + if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBIDN2_INCLUDE_DIR AND NOT DEFINED LIBIDN2_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBIDN2 "libidn2") + pkg_check_modules(LIBIDN2 ${LIBIDN2_PC_REQUIRES}) endif() if(LIBIDN2_FOUND) diff --git a/CMake/FindLibpsl.cmake b/CMake/FindLibpsl.cmake index bb323f4..0d96995 100644 --- a/CMake/FindLibpsl.cmake +++ b/CMake/FindLibpsl.cmake @@ -33,48 +33,50 @@ # - `LIBPSL_FOUND`: System has libpsl. # - `LIBPSL_INCLUDE_DIRS`: The libpsl include directories. # - `LIBPSL_LIBRARIES`: The libpsl library names. +# - `LIBPSL_LIBRARY_DIRS`: The libpsl library directories. +# - `LIBPSL_PC_REQUIRES`: The libpsl pkg-config packages. +# - `LIBPSL_CFLAGS`: Required compiler flags. # - `LIBPSL_VERSION`: Version of libpsl. -if(CURL_USE_PKGCONFIG) +set(LIBPSL_PC_REQUIRES "libpsl") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED LIBPSL_INCLUDE_DIR AND + NOT DEFINED LIBPSL_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_LIBPSL "libpsl") + pkg_check_modules(LIBPSL ${LIBPSL_PC_REQUIRES}) endif() -find_path(LIBPSL_INCLUDE_DIR NAMES "libpsl.h" - HINTS - ${PC_LIBPSL_INCLUDEDIR} - ${PC_LIBPSL_INCLUDE_DIRS} -) +if(LIBPSL_FOUND AND LIBPSL_INCLUDE_DIRS) + string(REPLACE ";" " " LIBPSL_CFLAGS "${LIBPSL_CFLAGS}") + message(STATUS "Found Libpsl (via pkg-config): ${LIBPSL_INCLUDE_DIRS} (found version \"${LIBPSL_VERSION}\")") +else() + find_path(LIBPSL_INCLUDE_DIR NAMES "libpsl.h") + find_library(LIBPSL_LIBRARY NAMES "psl" "libpsl") -find_library(LIBPSL_LIBRARY NAMES "psl" "libpsl" - HINTS - ${PC_LIBPSL_LIBDIR} - ${PC_LIBPSL_LIBRARY_DIRS} -) + unset(LIBPSL_VERSION CACHE) + if(LIBPSL_INCLUDE_DIR AND EXISTS "${LIBPSL_INCLUDE_DIR}/libpsl.h") + set(_version_regex "#[\t ]*define[\t ]+PSL_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${LIBPSL_INCLUDE_DIR}/libpsl.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(LIBPSL_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() -if(PC_LIBPSL_VERSION) - set(LIBPSL_VERSION ${PC_LIBPSL_VERSION}) -elseif(LIBPSL_INCLUDE_DIR AND EXISTS "${LIBPSL_INCLUDE_DIR}/libpsl.h") - set(_version_regex "#[\t ]*define[\t ]+PSL_VERSION[\t ]+\"([^\"]*)\"") - file(STRINGS "${LIBPSL_INCLUDE_DIR}/libpsl.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(LIBPSL_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libpsl + REQUIRED_VARS + LIBPSL_INCLUDE_DIR + LIBPSL_LIBRARY + VERSION_VAR + LIBPSL_VERSION + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libpsl - REQUIRED_VARS - LIBPSL_INCLUDE_DIR - LIBPSL_LIBRARY - VERSION_VAR - LIBPSL_VERSION -) + if(LIBPSL_FOUND) + set(LIBPSL_INCLUDE_DIRS ${LIBPSL_INCLUDE_DIR}) + set(LIBPSL_LIBRARIES ${LIBPSL_LIBRARY}) + endif() -if(LIBPSL_FOUND) - set(LIBPSL_INCLUDE_DIRS ${LIBPSL_INCLUDE_DIR}) - set(LIBPSL_LIBRARIES ${LIBPSL_LIBRARY}) + mark_as_advanced(LIBPSL_INCLUDE_DIR LIBPSL_LIBRARY) endif() - -mark_as_advanced(LIBPSL_INCLUDE_DIR LIBPSL_LIBRARY) diff --git a/CMake/FindLibrtmp.cmake b/CMake/FindLibrtmp.cmake new file mode 100644 index 0000000..dd3fb9e --- /dev/null +++ b/CMake/FindLibrtmp.cmake @@ -0,0 +1,102 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# Find the librtmp library +# +# Input variables: +# +# - `LIBRTMP_INCLUDE_DIR`: The librtmp include directory. +# - `LIBRTMP_LIBRARY`: Path to `librtmp` library. +# +# Result variables: +# +# - `LIBRTMP_FOUND`: System has librtmp. +# - `LIBRTMP_INCLUDE_DIRS`: The librtmp include directories. +# - `LIBRTMP_LIBRARIES`: The librtmp library names. +# - `LIBRTMP_LIBRARY_DIRS`: The librtmp library directories. +# - `LIBRTMP_PC_REQUIRES`: The librtmp pkg-config packages. +# - `LIBRTMP_CFLAGS`: Required compiler flags. +# - `LIBRTMP_VERSION`: Version of librtmp. + +set(LIBRTMP_PC_REQUIRES "librtmp") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED LIBRTMP_INCLUDE_DIR AND + NOT DEFINED LIBRTMP_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(LIBRTMP ${LIBRTMP_PC_REQUIRES}) +endif() + +if(LIBRTMP_FOUND AND LIBRTMP_INCLUDE_DIRS) + string(REPLACE ";" " " LIBRTMP_CFLAGS "${LIBRTMP_CFLAGS}") + message(STATUS "Found Librtmp (via pkg-config): ${LIBRTMP_INCLUDE_DIRS} (found version \"${LIBRTMP_VERSION}\")") +else() + find_path(LIBRTMP_INCLUDE_DIR NAMES "librtmp/rtmp.h") + find_library(LIBRTMP_LIBRARY NAMES "rtmp") + + unset(LIBRTMP_VERSION CACHE) + if(LIBRTMP_INCLUDE_DIR AND EXISTS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h") + set(_version_regex "#[\t ]*define[\t ]+RTMP_LIB_VERSION[\t ]+0x([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F]).*") + file(STRINGS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str1 "${_version_str}") + string(REGEX REPLACE "${_version_regex}" "\\2" _version_str2 "${_version_str}") + if(CMAKE_VERSION VERSION_LESS 3.13) + # No support for hex version numbers, just strip leading zeroes + string(REGEX REPLACE "^0" "" _version_str1 "${_version_str1}") + string(REGEX REPLACE "^0" "" _version_str2 "${_version_str2}") + else() + math(EXPR _version_str1 "0x${_version_str1}" OUTPUT_FORMAT DECIMAL) + math(EXPR _version_str2 "0x${_version_str2}" OUTPUT_FORMAT DECIMAL) + endif() + set(LIBRTMP_VERSION "${_version_str1}.${_version_str2}") + unset(_version_regex) + unset(_version_str1) + unset(_version_str2) + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Librtmp + REQUIRED_VARS + LIBRTMP_INCLUDE_DIR + LIBRTMP_LIBRARY + VERSION_VAR + LIBRTMP_VERSION + ) + + if(LIBRTMP_FOUND) + set(LIBRTMP_INCLUDE_DIRS ${LIBRTMP_INCLUDE_DIR}) + set(LIBRTMP_LIBRARIES ${LIBRTMP_LIBRARY}) + endif() + + mark_as_advanced(LIBRTMP_INCLUDE_DIR LIBRTMP_LIBRARY) + + # Necessary when linking a static librtmp + find_package(OpenSSL) + if(OPENSSL_FOUND) + list(APPEND LIBRTMP_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) + endif() +endif() + +if(LIBRTMP_FOUND AND WIN32) + list(APPEND LIBRTMP_LIBRARIES "winmm") +endif() diff --git a/CMake/FindLibssh.cmake b/CMake/FindLibssh.cmake index 2b95fd8..b8b4a51 100644 --- a/CMake/FindLibssh.cmake +++ b/CMake/FindLibssh.cmake @@ -25,23 +25,26 @@ # # Input variables: # -# LIBSSH_INCLUDE_DIR The libssh include directory. -# LIBSSH_LIBRARY Path to libssh library. +# - `LIBSSH_INCLUDE_DIR`: The libssh include directory. +# - `LIBSSH_LIBRARY`: Path to libssh library. # # Result variables: # -# LIBSSH_FOUND System has libssh. -# LIBSSH_INCLUDE_DIRS The libssh include directories. -# LIBSSH_LIBRARIES The libssh library names. -# LIBSSH_LIBRARY_DIRS The libssh library directories. -# LIBSSH_CFLAGS Required compiler flags. -# LIBSSH_VERSION Version of libssh. +# - `LIBSSH_FOUND`: System has libssh. +# - `LIBSSH_INCLUDE_DIRS`: The libssh include directories. +# - `LIBSSH_LIBRARIES`: The libssh library names. +# - `LIBSSH_LIBRARY_DIRS`: The libssh library directories. +# - `LIBSSH_PC_REQUIRES`: The libssh pkg-config packages. +# - `LIBSSH_CFLAGS`: Required compiler flags. +# - `LIBSSH_VERSION`: Version of libssh. + +set(LIBSSH_PC_REQUIRES "libssh") if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBSSH_INCLUDE_DIR AND NOT DEFINED LIBSSH_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBSSH "libssh") + pkg_check_modules(LIBSSH ${LIBSSH_PC_REQUIRES}) endif() if(LIBSSH_FOUND) diff --git a/CMake/FindLibssh2.cmake b/CMake/FindLibssh2.cmake index 0785214..dfb0582 100644 --- a/CMake/FindLibssh2.cmake +++ b/CMake/FindLibssh2.cmake @@ -33,48 +33,50 @@ # - `LIBSSH2_FOUND`: System has libssh2. # - `LIBSSH2_INCLUDE_DIRS`: The libssh2 include directories. # - `LIBSSH2_LIBRARIES`: The libssh2 library names. +# - `LIBSSH2_LIBRARY_DIRS`: The libssh2 library directories. +# - `LIBSSH2_PC_REQUIRES`: The libssh2 pkg-config packages. +# - `LIBSSH2_CFLAGS`: Required compiler flags. # - `LIBSSH2_VERSION`: Version of libssh2. -if(CURL_USE_PKGCONFIG) +set(LIBSSH2_PC_REQUIRES "libssh2") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED LIBSSH2_INCLUDE_DIR AND + NOT DEFINED LIBSSH2_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_LIBSSH2 "libssh2") + pkg_check_modules(LIBSSH2 ${LIBSSH2_PC_REQUIRES}) endif() -find_path(LIBSSH2_INCLUDE_DIR NAMES "libssh2.h" - HINTS - ${PC_LIBSSH2_INCLUDEDIR} - ${PC_LIBSSH2_INCLUDE_DIRS} -) +if(LIBSSH2_FOUND AND LIBSSH2_INCLUDE_DIRS) + string(REPLACE ";" " " LIBSSH2_CFLAGS "${LIBSSH2_CFLAGS}") + message(STATUS "Found Libssh2 (via pkg-config): ${LIBSSH2_INCLUDE_DIRS} (found version \"${LIBSSH2_VERSION}\")") +else() + find_path(LIBSSH2_INCLUDE_DIR NAMES "libssh2.h") + find_library(LIBSSH2_LIBRARY NAMES "ssh2" "libssh2") -find_library(LIBSSH2_LIBRARY NAMES "ssh2" "libssh2" - HINTS - ${PC_LIBSSH2_LIBDIR} - ${PC_LIBSSH2_LIBRARY_DIRS} -) + unset(LIBSSH2_VERSION CACHE) + if(LIBSSH2_INCLUDE_DIR AND EXISTS "${LIBSSH2_INCLUDE_DIR}/libssh2.h") + set(_version_regex "#[\t ]*define[\t ]+LIBSSH2_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${LIBSSH2_INCLUDE_DIR}/libssh2.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(LIBSSH2_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() -if(PC_LIBSSH2_VERSION) - set(LIBSSH2_VERSION ${PC_LIBSSH2_VERSION}) -elseif(LIBSSH2_INCLUDE_DIR AND EXISTS "${LIBSSH2_INCLUDE_DIR}/libssh2.h") - set(_version_regex "#[\t ]*define[\t ]+LIBSSH2_VERSION[\t ]+\"([^\"]*)\"") - file(STRINGS "${LIBSSH2_INCLUDE_DIR}/libssh2.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(LIBSSH2_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libssh2 + REQUIRED_VARS + LIBSSH2_INCLUDE_DIR + LIBSSH2_LIBRARY + VERSION_VAR + LIBSSH2_VERSION + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libssh2 - REQUIRED_VARS - LIBSSH2_INCLUDE_DIR - LIBSSH2_LIBRARY - VERSION_VAR - LIBSSH2_VERSION -) + if(LIBSSH2_FOUND) + set(LIBSSH2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR}) + set(LIBSSH2_LIBRARIES ${LIBSSH2_LIBRARY}) + endif() -if(LIBSSH2_FOUND) - set(LIBSSH2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR}) - set(LIBSSH2_LIBRARIES ${LIBSSH2_LIBRARY}) + mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY) endif() - -mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY) diff --git a/CMake/FindLibuv.cmake b/CMake/FindLibuv.cmake index d647e34..8255eaa 100644 --- a/CMake/FindLibuv.cmake +++ b/CMake/FindLibuv.cmake @@ -34,14 +34,17 @@ # - `LIBUV_INCLUDE_DIRS`: The libuv include directories. # - `LIBUV_LIBRARIES`: The libuv library names. # - `LIBUV_LIBRARY_DIRS`: The libuv library directories. +# - `LIBUV_PC_REQUIRES`: The libuv pkg-config packages. # - `LIBUV_CFLAGS`: Required compiler flags. # - `LIBUV_VERSION`: Version of libuv. +set(LIBUV_PC_REQUIRES "libuv") + if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBUV_INCLUDE_DIR AND NOT DEFINED LIBUV_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBUV "libuv") + pkg_check_modules(LIBUV ${LIBUV_PC_REQUIRES}) endif() if(LIBUV_FOUND) diff --git a/CMake/FindMSH3.cmake b/CMake/FindMSH3.cmake index 387d30b..da38d45 100644 --- a/CMake/FindMSH3.cmake +++ b/CMake/FindMSH3.cmake @@ -38,18 +38,21 @@ # - `MSH3_CFLAGS`: Required compiler flags. # - `MSH3_VERSION`: Version of msh3. +set(MSH3_PC_REQUIRES "libmsh3") + if(CURL_USE_PKGCONFIG AND NOT DEFINED MSH3_INCLUDE_DIR AND NOT DEFINED MSH3_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(MSH3 "libmsh3") + pkg_check_modules(MSH3 ${MSH3_PC_REQUIRES}) endif() if(MSH3_FOUND) - set(MSH3_PC_REQUIRES "libmsh3") string(REPLACE ";" " " MSH3_CFLAGS "${MSH3_CFLAGS}") message(STATUS "Found MSH3 (via pkg-config): ${MSH3_INCLUDE_DIRS} (found version \"${MSH3_VERSION}\")") else() + set(MSH3_PC_REQUIRES "") # Depend on pkg-config only when found via pkg-config + find_path(MSH3_INCLUDE_DIR NAMES "msh3.h") find_library(MSH3_LIBRARY NAMES "msh3") diff --git a/CMake/FindMbedTLS.cmake b/CMake/FindMbedTLS.cmake index e361c96..c3ba27d 100644 --- a/CMake/FindMbedTLS.cmake +++ b/CMake/FindMbedTLS.cmake @@ -46,24 +46,29 @@ if(DEFINED MBEDTLS_INCLUDE_DIRS AND NOT DEFINED MBEDTLS_INCLUDE_DIR) unset(MBEDTLS_INCLUDE_DIRS) endif() +set(MBEDTLS_PC_REQUIRES "mbedtls") + if(CURL_USE_PKGCONFIG AND NOT DEFINED MBEDTLS_INCLUDE_DIR AND NOT DEFINED MBEDTLS_LIBRARY AND NOT DEFINED MBEDX509_LIBRARY AND NOT DEFINED MBEDCRYPTO_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(MBEDTLS "mbedtls") + pkg_check_modules(MBEDTLS ${MBEDTLS_PC_REQUIRES}) pkg_check_modules(MBEDX509 "mbedx509") pkg_check_modules(MBEDCRYPTO "mbedcrypto") endif() if(MBEDTLS_FOUND AND MBEDX509_FOUND AND MBEDCRYPTO_FOUND) list(APPEND MBEDTLS_LIBRARIES ${MBEDX509_LIBRARIES} ${MBEDCRYPTO_LIBRARIES}) + list(REVERSE MBEDTLS_LIBRARIES) list(REMOVE_DUPLICATES MBEDTLS_LIBRARIES) - set(MBEDTLS_PC_REQUIRES "mbedtls") + list(REVERSE MBEDTLS_LIBRARIES) string(REPLACE ";" " " MBEDTLS_CFLAGS "${MBEDTLS_CFLAGS}") message(STATUS "Found MbedTLS (via pkg-config): ${MBEDTLS_INCLUDE_DIRS} (found version \"${MBEDTLS_VERSION}\")") else() + set(MBEDTLS_PC_REQUIRES "") # Depend on pkg-config only when found via pkg-config + find_path(MBEDTLS_INCLUDE_DIR NAMES "mbedtls/ssl.h") find_library(MBEDTLS_LIBRARY NAMES "mbedtls" "libmbedtls") find_library(MBEDX509_LIBRARY NAMES "mbedx509" "libmbedx509") diff --git a/CMake/FindNGHTTP2.cmake b/CMake/FindNGHTTP2.cmake index a0aacbf..b8f37fd 100644 --- a/CMake/FindNGHTTP2.cmake +++ b/CMake/FindNGHTTP2.cmake @@ -33,48 +33,50 @@ # - `NGHTTP2_FOUND`: System has nghttp2. # - `NGHTTP2_INCLUDE_DIRS`: The nghttp2 include directories. # - `NGHTTP2_LIBRARIES`: The nghttp2 library names. +# - `NGHTTP2_LIBRARY_DIRS`: The nghttp2 library directories. +# - `NGHTTP2_PC_REQUIRES`: The nghttp2 pkg-config packages. +# - `NGHTTP2_CFLAGS`: Required compiler flags. # - `NGHTTP2_VERSION`: Version of nghttp2. -if(CURL_USE_PKGCONFIG) +set(NGHTTP2_PC_REQUIRES "libnghttp2") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED NGHTTP2_INCLUDE_DIR AND + NOT DEFINED NGHTTP2_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_NGHTTP2 "libnghttp2") + pkg_check_modules(NGHTTP2 ${NGHTTP2_PC_REQUIRES}) endif() -find_path(NGHTTP2_INCLUDE_DIR NAMES "nghttp2/nghttp2.h" - HINTS - ${PC_NGHTTP2_INCLUDEDIR} - ${PC_NGHTTP2_INCLUDE_DIRS} -) +if(NGHTTP2_FOUND) + string(REPLACE ";" " " NGHTTP2_CFLAGS "${NGHTTP2_CFLAGS}") + message(STATUS "Found NGHTTP2 (via pkg-config): ${NGHTTP2_INCLUDE_DIRS} (found version \"${NGHTTP2_VERSION}\")") +else() + find_path(NGHTTP2_INCLUDE_DIR NAMES "nghttp2/nghttp2.h") + find_library(NGHTTP2_LIBRARY NAMES "nghttp2" "nghttp2_static") -find_library(NGHTTP2_LIBRARY NAMES "nghttp2" "nghttp2_static" - HINTS - ${PC_NGHTTP2_LIBDIR} - ${PC_NGHTTP2_LIBRARY_DIRS} -) + unset(NGHTTP2_VERSION CACHE) + if(NGHTTP2_INCLUDE_DIR AND EXISTS "${NGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2ver.h") + set(_version_regex "#[\t ]*define[\t ]+NGHTTP2_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${NGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2ver.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(NGHTTP2_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() -if(PC_NGHTTP2_VERSION) - set(NGHTTP2_VERSION ${PC_NGHTTP2_VERSION}) -elseif(NGHTTP2_INCLUDE_DIR AND EXISTS "${NGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2ver.h") - set(_version_regex "#[\t ]*define[\t ]+NGHTTP2_VERSION[\t ]+\"([^\"]*)\"") - file(STRINGS "${NGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2ver.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(NGHTTP2_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(NGHTTP2 + REQUIRED_VARS + NGHTTP2_INCLUDE_DIR + NGHTTP2_LIBRARY + VERSION_VAR + NGHTTP2_VERSION + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(NGHTTP2 - REQUIRED_VARS - NGHTTP2_INCLUDE_DIR - NGHTTP2_LIBRARY - VERSION_VAR - NGHTTP2_VERSION -) + if(NGHTTP2_FOUND) + set(NGHTTP2_INCLUDE_DIRS ${NGHTTP2_INCLUDE_DIR}) + set(NGHTTP2_LIBRARIES ${NGHTTP2_LIBRARY}) + endif() -if(NGHTTP2_FOUND) - set(NGHTTP2_INCLUDE_DIRS ${NGHTTP2_INCLUDE_DIR}) - set(NGHTTP2_LIBRARIES ${NGHTTP2_LIBRARY}) + mark_as_advanced(NGHTTP2_INCLUDE_DIR NGHTTP2_LIBRARY) endif() - -mark_as_advanced(NGHTTP2_INCLUDE_DIR NGHTTP2_LIBRARY) diff --git a/CMake/FindNGHTTP3.cmake b/CMake/FindNGHTTP3.cmake index 1adbd60..99edd19 100644 --- a/CMake/FindNGHTTP3.cmake +++ b/CMake/FindNGHTTP3.cmake @@ -33,48 +33,50 @@ # - `NGHTTP3_FOUND`: System has nghttp3. # - `NGHTTP3_INCLUDE_DIRS`: The nghttp3 include directories. # - `NGHTTP3_LIBRARIES`: The nghttp3 library names. +# - `NGHTTP3_LIBRARY_DIRS`: The nghttp3 library directories. +# - `NGHTTP3_PC_REQUIRES`: The nghttp3 pkg-config packages. +# - `NGHTTP3_CFLAGS`: Required compiler flags. # - `NGHTTP3_VERSION`: Version of nghttp3. -if(CURL_USE_PKGCONFIG) +set(NGHTTP3_PC_REQUIRES "libnghttp3") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED NGHTTP3_INCLUDE_DIR AND + NOT DEFINED NGHTTP3_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_NGHTTP3 "libnghttp3") + pkg_check_modules(NGHTTP3 ${NGHTTP3_PC_REQUIRES}) endif() -find_path(NGHTTP3_INCLUDE_DIR NAMES "nghttp3/nghttp3.h" - HINTS - ${PC_NGHTTP3_INCLUDEDIR} - ${PC_NGHTTP3_INCLUDE_DIRS} -) +if(NGHTTP3_FOUND) + string(REPLACE ";" " " NGHTTP3_CFLAGS "${NGHTTP3_CFLAGS}") + message(STATUS "Found NGHTTP3 (via pkg-config): ${NGHTTP3_INCLUDE_DIRS} (found version \"${NGHTTP3_VERSION}\")") +else() + find_path(NGHTTP3_INCLUDE_DIR NAMES "nghttp3/nghttp3.h") + find_library(NGHTTP3_LIBRARY NAMES "nghttp3") -find_library(NGHTTP3_LIBRARY NAMES "nghttp3" - HINTS - ${PC_NGHTTP3_LIBDIR} - ${PC_NGHTTP3_LIBRARY_DIRS} -) + unset(NGHTTP3_VERSION CACHE) + if(NGHTTP3_INCLUDE_DIR AND EXISTS "${NGHTTP3_INCLUDE_DIR}/nghttp3/version.h") + set(_version_regex "#[\t ]*define[\t ]+NGHTTP3_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${NGHTTP3_INCLUDE_DIR}/nghttp3/version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(NGHTTP3_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() -if(PC_NGHTTP3_VERSION) - set(NGHTTP3_VERSION ${PC_NGHTTP3_VERSION}) -elseif(NGHTTP3_INCLUDE_DIR AND EXISTS "${NGHTTP3_INCLUDE_DIR}/nghttp3/version.h") - set(_version_regex "#[\t ]*define[\t ]+NGHTTP3_VERSION[\t ]+\"([^\"]*)\"") - file(STRINGS "${NGHTTP3_INCLUDE_DIR}/nghttp3/version.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(NGHTTP3_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(NGHTTP3 + REQUIRED_VARS + NGHTTP3_INCLUDE_DIR + NGHTTP3_LIBRARY + VERSION_VAR + NGHTTP3_VERSION + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(NGHTTP3 - REQUIRED_VARS - NGHTTP3_INCLUDE_DIR - NGHTTP3_LIBRARY - VERSION_VAR - NGHTTP3_VERSION -) + if(NGHTTP3_FOUND) + set(NGHTTP3_INCLUDE_DIRS ${NGHTTP3_INCLUDE_DIR}) + set(NGHTTP3_LIBRARIES ${NGHTTP3_LIBRARY}) + endif() -if(NGHTTP3_FOUND) - set(NGHTTP3_INCLUDE_DIRS ${NGHTTP3_INCLUDE_DIR}) - set(NGHTTP3_LIBRARIES ${NGHTTP3_LIBRARY}) + mark_as_advanced(NGHTTP3_INCLUDE_DIR NGHTTP3_LIBRARY) endif() - -mark_as_advanced(NGHTTP3_INCLUDE_DIR NGHTTP3_LIBRARY) diff --git a/CMake/FindNGTCP2.cmake b/CMake/FindNGTCP2.cmake index 99b022d..0cbb348 100644 --- a/CMake/FindNGTCP2.cmake +++ b/CMake/FindNGTCP2.cmake @@ -41,36 +41,11 @@ # - `NGTCP2_FOUND`: System has ngtcp2. # - `NGTCP2_INCLUDE_DIRS`: The ngtcp2 include directories. # - `NGTCP2_LIBRARIES`: The ngtcp2 library names. +# - `NGTCP2_LIBRARY_DIRS`: The ngtcp2 library directories. +# - `NGTCP2_PC_REQUIRES`: The ngtcp2 pkg-config packages. +# - `NGTCP2_CFLAGS`: Required compiler flags. # - `NGTCP2_VERSION`: Version of ngtcp2. -if(CURL_USE_PKGCONFIG) - find_package(PkgConfig QUIET) - pkg_check_modules(PC_NGTCP2 "libngtcp2") -endif() - -find_path(NGTCP2_INCLUDE_DIR NAMES "ngtcp2/ngtcp2.h" - HINTS - ${PC_NGTCP2_INCLUDEDIR} - ${PC_NGTCP2_INCLUDE_DIRS} -) - -find_library(NGTCP2_LIBRARY NAMES "ngtcp2" - HINTS - ${PC_NGTCP2_LIBDIR} - ${PC_NGTCP2_LIBRARY_DIRS} -) - -if(PC_NGTCP2_VERSION) - set(NGTCP2_VERSION ${PC_NGTCP2_VERSION}) -elseif(NGTCP2_INCLUDE_DIR AND EXISTS "${NGTCP2_INCLUDE_DIR}/ngtcp2/version.h") - set(_version_regex "#[\t ]*define[\t ]+NGTCP2_VERSION[\t ]+\"([^\"]*)\"") - file(STRINGS "${NGTCP2_INCLUDE_DIR}/ngtcp2/version.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(NGTCP2_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) -endif() - if(NGTCP2_FIND_COMPONENTS) set(_ngtcp2_crypto_backend "") foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS) @@ -83,40 +58,73 @@ if(NGTCP2_FIND_COMPONENTS) endforeach() if(_ngtcp2_crypto_backend) - string(TOLOWER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library) + string(TOLOWER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library_lower) + string(TOUPPER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library_upper) + endif() +endif() - if(CURL_USE_PKGCONFIG) - pkg_check_modules(PC_${_crypto_library} "lib${_crypto_library}") - endif() +set(NGTCP2_PC_REQUIRES "libngtcp2") +if(_ngtcp2_crypto_backend) + set(NGTCP2_CRYPTO_PC_REQUIRES "lib${_crypto_library_lower}") +endif() +if(CURL_USE_PKGCONFIG AND + NOT DEFINED NGTCP2_INCLUDE_DIR AND + NOT DEFINED NGTCP2_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(NGTCP2 ${NGTCP2_PC_REQUIRES}) + if(_ngtcp2_crypto_backend) + pkg_check_modules("${_crypto_library_upper}" ${NGTCP2_CRYPTO_PC_REQUIRES}) + else() + set("${_crypto_library_upper}_FOUND" TRUE) + endif() +endif() + +list(APPEND NGTCP2_PC_REQUIRES ${NGTCP2_CRYPTO_PC_REQUIRES}) + +if(NGTCP2_FOUND AND "${${_crypto_library_upper}_FOUND}") + list(APPEND NGTCP2_LIBRARIES "${${_crypto_library_upper}_LIBRARIES}") + list(REMOVE_DUPLICATES NGTCP2_LIBRARIES) + string(REPLACE ";" " " NGTCP2_CFLAGS "${NGTCP2_CFLAGS}") + message(STATUS "Found NGTCP2 (via pkg-config): ${NGTCP2_INCLUDE_DIRS} (found version \"${NGTCP2_VERSION}\")") +else() + find_path(NGTCP2_INCLUDE_DIR NAMES "ngtcp2/ngtcp2.h") + find_library(NGTCP2_LIBRARY NAMES "ngtcp2") + + unset(NGTCP2_VERSION CACHE) + if(NGTCP2_INCLUDE_DIR AND EXISTS "${NGTCP2_INCLUDE_DIR}/ngtcp2/version.h") + set(_version_regex "#[\t ]*define[\t ]+NGTCP2_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${NGTCP2_INCLUDE_DIR}/ngtcp2/version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(NGTCP2_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() + + if(_ngtcp2_crypto_backend) get_filename_component(_ngtcp2_library_dir "${NGTCP2_LIBRARY}" DIRECTORY) - find_library(${_crypto_library}_LIBRARY NAMES ${_crypto_library} - HINTS - ${_ngtcp2_library_dir} - ${PC_${_crypto_library}_LIBDIR} - ${PC_${_crypto_library}_LIBRARY_DIRS} - ) + find_library(${_crypto_library_upper}_LIBRARY NAMES ${_crypto_library_lower} HINTS ${_ngtcp2_library_dir}) - if(${_crypto_library}_LIBRARY) + if(${_crypto_library_upper}_LIBRARY) set(NGTCP2_${_ngtcp2_crypto_backend}_FOUND TRUE) - set(NGTCP2_CRYPTO_LIBRARY ${${_crypto_library}_LIBRARY}) + set(NGTCP2_CRYPTO_LIBRARY ${${_crypto_library_upper}_LIBRARY}) endif() endif() -endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(NGTCP2 - REQUIRED_VARS - NGTCP2_INCLUDE_DIR - NGTCP2_LIBRARY - VERSION_VAR - NGTCP2_VERSION - HANDLE_COMPONENTS -) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(NGTCP2 + REQUIRED_VARS + NGTCP2_INCLUDE_DIR + NGTCP2_LIBRARY + VERSION_VAR + NGTCP2_VERSION + HANDLE_COMPONENTS + ) -if(NGTCP2_FOUND) - set(NGTCP2_INCLUDE_DIRS ${NGTCP2_INCLUDE_DIR}) - set(NGTCP2_LIBRARIES ${NGTCP2_LIBRARY} ${NGTCP2_CRYPTO_LIBRARY}) -endif() + if(NGTCP2_FOUND) + set(NGTCP2_INCLUDE_DIRS ${NGTCP2_INCLUDE_DIR}) + set(NGTCP2_LIBRARIES ${NGTCP2_LIBRARY} ${NGTCP2_CRYPTO_LIBRARY}) + endif() -mark_as_advanced(NGTCP2_INCLUDE_DIR NGTCP2_LIBRARY NGTCP2_CRYPTO_LIBRARY) + mark_as_advanced(NGTCP2_INCLUDE_DIR NGTCP2_LIBRARY NGTCP2_CRYPTO_LIBRARY) +endif() diff --git a/CMake/FindNettle.cmake b/CMake/FindNettle.cmake index 56f2a94..753633a 100644 --- a/CMake/FindNettle.cmake +++ b/CMake/FindNettle.cmake @@ -34,14 +34,17 @@ # - `NETTLE_INCLUDE_DIRS`: The nettle include directories. # - `NETTLE_LIBRARIES`: The nettle library names. # - `NETTLE_LIBRARY_DIRS`: The nettle library directories. +# - `NETTLE_PC_REQUIRES`: The nettle pkg-config packages. # - `NETTLE_CFLAGS`: Required compiler flags. # - `NETTLE_VERSION`: Version of nettle. +set(NETTLE_PC_REQUIRES "nettle") + if(CURL_USE_PKGCONFIG AND NOT DEFINED NETTLE_INCLUDE_DIR AND NOT DEFINED NETTLE_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(NETTLE "nettle") + pkg_check_modules(NETTLE ${NETTLE_PC_REQUIRES}) endif() if(NETTLE_FOUND) diff --git a/CMake/FindQuiche.cmake b/CMake/FindQuiche.cmake index 6db5cb0..092aff8 100644 --- a/CMake/FindQuiche.cmake +++ b/CMake/FindQuiche.cmake @@ -34,14 +34,17 @@ # - `QUICHE_INCLUDE_DIRS`: The quiche include directories. # - `QUICHE_LIBRARIES`: The quiche library names. # - `QUICHE_LIBRARY_DIRS`: The quiche library directories. +# - `QUICHE_PC_REQUIRES`: The quiche pkg-config packages. # - `QUICHE_CFLAGS`: Required compiler flags. # - `QUICHE_VERSION`: Version of quiche. +set(QUICHE_PC_REQUIRES "quiche") + if(CURL_USE_PKGCONFIG AND NOT DEFINED QUICHE_INCLUDE_DIR AND NOT DEFINED QUICHE_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(QUICHE "quiche") + pkg_check_modules(QUICHE ${QUICHE_PC_REQUIRES}) endif() if(QUICHE_FOUND) diff --git a/CMake/FindRustls.cmake b/CMake/FindRustls.cmake index db0e153..51f4506 100644 --- a/CMake/FindRustls.cmake +++ b/CMake/FindRustls.cmake @@ -38,18 +38,21 @@ # - `RUSTLS_CFLAGS`: Required compiler flags. # - `RUSTLS_VERSION`: Version of Rustls. +set(RUSTLS_PC_REQUIRES "rustls") + if(CURL_USE_PKGCONFIG AND NOT DEFINED RUSTLS_INCLUDE_DIR AND NOT DEFINED RUSTLS_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(RUSTLS "rustls") + pkg_check_modules(RUSTLS ${RUSTLS_PC_REQUIRES}) endif() if(RUSTLS_FOUND) - set(RUSTLS_PC_REQUIRES "rustls") string(REPLACE ";" " " RUSTLS_CFLAGS "${RUSTLS_CFLAGS}") message(STATUS "Found Rustls (via pkg-config): ${RUSTLS_INCLUDE_DIRS} (found version \"${RUSTLS_VERSION}\")") else() + set(RUSTLS_PC_REQUIRES "") # Depend on pkg-config only when found via pkg-config + find_path(RUSTLS_INCLUDE_DIR NAMES "rustls.h") find_library(RUSTLS_LIBRARY NAMES "rustls") @@ -68,36 +71,38 @@ else() mark_as_advanced(RUSTLS_INCLUDE_DIR RUSTLS_LIBRARY) endif() -if(APPLE) - find_library(SECURITY_FRAMEWORK "Security") - mark_as_advanced(SECURITY_FRAMEWORK) - if(NOT SECURITY_FRAMEWORK) - message(FATAL_ERROR "Security framework not found") - endif() - list(APPEND RUSTLS_LIBRARIES "-framework Security") +if(RUSTLS_FOUND) + if(APPLE) + find_library(SECURITY_FRAMEWORK NAMES "Security") + mark_as_advanced(SECURITY_FRAMEWORK) + if(NOT SECURITY_FRAMEWORK) + message(FATAL_ERROR "Security framework not found") + endif() + list(APPEND RUSTLS_LIBRARIES "-framework Security") - find_library(FOUNDATION_FRAMEWORK "Foundation") - mark_as_advanced(FOUNDATION_FRAMEWORK) - if(NOT FOUNDATION_FRAMEWORK) - message(FATAL_ERROR "Foundation framework not found") - endif() - list(APPEND RUSTLS_LIBRARIES "-framework Foundation") -elseif(NOT WIN32) - find_library(_pthread_library "pthread") - if(_pthread_library) - list(APPEND RUSTLS_LIBRARIES "pthread") - endif() - mark_as_advanced(_pthread_library) + find_library(FOUNDATION_FRAMEWORK NAMES "Foundation") + mark_as_advanced(FOUNDATION_FRAMEWORK) + if(NOT FOUNDATION_FRAMEWORK) + message(FATAL_ERROR "Foundation framework not found") + endif() + list(APPEND RUSTLS_LIBRARIES "-framework Foundation") + elseif(NOT WIN32) + find_library(PTHREAD_LIBRARY NAMES "pthread") + if(PTHREAD_LIBRARY) + list(APPEND RUSTLS_LIBRARIES ${PTHREAD_LIBRARY}) + endif() + mark_as_advanced(PTHREAD_LIBRARY) - find_library(_dl_library "dl") - if(_dl_library) - list(APPEND RUSTLS_LIBRARIES "dl") - endif() - mark_as_advanced(_dl_library) + find_library(DL_LIBRARY NAMES "dl") + if(DL_LIBRARY) + list(APPEND RUSTLS_LIBRARIES ${DL_LIBRARY}) + endif() + mark_as_advanced(DL_LIBRARY) - find_library(_math_library "m") - if(_math_library) - list(APPEND RUSTLS_LIBRARIES "m") + find_library(MATH_LIBRARY NAMES "m") + if(MATH_LIBRARY) + list(APPEND RUSTLS_LIBRARIES ${MATH_LIBRARY}) + endif() + mark_as_advanced(MATH_LIBRARY) endif() - mark_as_advanced(_math_library) endif() diff --git a/CMake/FindWolfSSL.cmake b/CMake/FindWolfSSL.cmake index 8f6f964..3163fee 100644 --- a/CMake/FindWolfSSL.cmake +++ b/CMake/FindWolfSSL.cmake @@ -34,6 +34,7 @@ # - `WOLFSSL_INCLUDE_DIRS`: The wolfSSL include directories. # - `WOLFSSL_LIBRARIES`: The wolfSSL library names. # - `WOLFSSL_LIBRARY_DIRS`: The wolfSSL library directories. +# - `WOLFSSL_PC_REQUIRES`: The wolfSSL pkg-config packages. # - `WOLFSSL_CFLAGS`: Required compiler flags. # - `WOLFSSL_VERSION`: Version of wolfSSL. @@ -46,11 +47,13 @@ if(DEFINED WolfSSL_LIBRARY AND NOT DEFINED WOLFSSL_LIBRARY) set(WOLFSSL_LIBRARY "${WolfSSL_LIBRARY}") endif() +set(WOLFSSL_PC_REQUIRES "wolfssl") + if(CURL_USE_PKGCONFIG AND NOT DEFINED WOLFSSL_INCLUDE_DIR AND NOT DEFINED WOLFSSL_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(WOLFSSL "wolfssl") + pkg_check_modules(WOLFSSL ${WOLFSSL_PC_REQUIRES}) endif() if(WOLFSSL_FOUND) @@ -87,10 +90,10 @@ else() mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY) endif() -if(NOT WIN32) - find_library(_math_library "m") - if(_math_library) - list(APPEND WOLFSSL_LIBRARIES "m") # for log and pow +if(WOLFSSL_FOUND AND NOT WIN32) + find_library(MATH_LIBRARY NAMES "m") + if(MATH_LIBRARY) + list(APPEND WOLFSSL_LIBRARIES ${MATH_LIBRARY}) # for log and pow endif() - mark_as_advanced(_math_library) + mark_as_advanced(MATH_LIBRARY) endif() diff --git a/CMake/FindZstd.cmake b/CMake/FindZstd.cmake index 7b0f557..dbfede7 100644 --- a/CMake/FindZstd.cmake +++ b/CMake/FindZstd.cmake @@ -33,6 +33,9 @@ # - `ZSTD_FOUND`: System has zstd. # - `ZSTD_INCLUDE_DIRS`: The zstd include directories. # - `ZSTD_LIBRARIES`: The zstd library names. +# - `ZSTD_LIBRARY_DIRS`: The zstd library directories. +# - `ZSTD_PC_REQUIRES`: The zstd pkg-config packages. +# - `ZSTD_CFLAGS`: Required compiler flags. # - `ZSTD_VERSION`: Version of zstd. if(DEFINED Zstd_INCLUDE_DIR AND NOT DEFINED ZSTD_INCLUDE_DIR) @@ -44,56 +47,55 @@ if(DEFINED Zstd_LIBRARY AND NOT DEFINED ZSTD_LIBRARY) set(ZSTD_LIBRARY "${Zstd_LIBRARY}") endif() -if(CURL_USE_PKGCONFIG) +set(ZSTD_PC_REQUIRES "libzstd") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED ZSTD_INCLUDE_DIR AND + NOT DEFINED ZSTD_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_ZSTD "libzstd") + pkg_check_modules(ZSTD ${ZSTD_PC_REQUIRES}) endif() -find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h" - HINTS - ${PC_ZSTD_INCLUDEDIR} - ${PC_ZSTD_INCLUDE_DIRS} -) +if(ZSTD_FOUND) + string(REPLACE ";" " " ZSTD_CFLAGS "${ZSTD_CFLAGS}") + message(STATUS "Found Zstd (via pkg-config): ${ZSTD_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")") +else() + find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h") + find_library(ZSTD_LIBRARY NAMES "zstd") -find_library(ZSTD_LIBRARY NAMES "zstd" - HINTS - ${PC_ZSTD_LIBDIR} - ${PC_ZSTD_LIBRARY_DIRS} -) + unset(ZSTD_VERSION CACHE) + if(ZSTD_INCLUDE_DIR AND EXISTS "${ZSTD_INCLUDE_DIR}/zstd.h") + set(_version_regex1 "#[\t ]*define[ \t]+ZSTD_VERSION_MAJOR[ \t]+([0-9]+).*") + set(_version_regex2 "#[\t ]*define[ \t]+ZSTD_VERSION_MINOR[ \t]+([0-9]+).*") + set(_version_regex3 "#[\t ]*define[ \t]+ZSTD_VERSION_RELEASE[ \t]+([0-9]+).*") + file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str1 REGEX "${_version_regex1}") + file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str2 REGEX "${_version_regex2}") + file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str3 REGEX "${_version_regex3}") + string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") + string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") + string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") + set(ZSTD_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") + unset(_version_regex1) + unset(_version_regex2) + unset(_version_regex3) + unset(_version_str1) + unset(_version_str2) + unset(_version_str3) + endif() -if(PC_ZSTD_VERSION) - set(ZSTD_VERSION ${PC_ZSTD_VERSION}) -elseif(ZSTD_INCLUDE_DIR AND EXISTS "${ZSTD_INCLUDE_DIR}/zstd.h") - set(_version_regex1 "#[\t ]*define[ \t]+ZSTD_VERSION_MAJOR[ \t]+([0-9]+).*") - set(_version_regex2 "#[\t ]*define[ \t]+ZSTD_VERSION_MINOR[ \t]+([0-9]+).*") - set(_version_regex3 "#[\t ]*define[ \t]+ZSTD_VERSION_RELEASE[ \t]+([0-9]+).*") - file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str1 REGEX "${_version_regex1}") - file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str2 REGEX "${_version_regex2}") - file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str3 REGEX "${_version_regex3}") - string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") - string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") - string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") - set(ZSTD_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") - unset(_version_regex1) - unset(_version_regex2) - unset(_version_regex3) - unset(_version_str1) - unset(_version_str2) - unset(_version_str3) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Zstd + REQUIRED_VARS + ZSTD_INCLUDE_DIR + ZSTD_LIBRARY + VERSION_VAR + ZSTD_VERSION + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Zstd - REQUIRED_VARS - ZSTD_INCLUDE_DIR - ZSTD_LIBRARY - VERSION_VAR - ZSTD_VERSION -) + if(ZSTD_FOUND) + set(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) + set(ZSTD_LIBRARIES ${ZSTD_LIBRARY}) + endif() -if(ZSTD_FOUND) - set(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) - set(ZSTD_LIBRARIES ${ZSTD_LIBRARY}) + mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) endif() - -mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index 7877f4c..ad0df18 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -27,7 +27,7 @@ # This macro is intended to be called multiple times with a sequence of # possibly dependent header files. Some headers depend on others to be # compiled correctly. -macro(check_include_file_concat _file _variable) +macro(check_include_file_concat_curl _file _variable) check_include_files("${CURL_INCLUDES};${_file}" ${_variable}) if(${_variable}) list(APPEND CURL_INCLUDES ${_file}) @@ -62,21 +62,25 @@ macro(curl_internal_test _curl_test) endif() endmacro() -macro(curl_dependency_option _dependency) - set(CURL_${_dependency} "AUTO" CACHE STRING "Build curl with ${_dependency} support (AUTO, ON or OFF)") - set_property(CACHE CURL_${_dependency} PROPERTY STRINGS "AUTO" "ON" "OFF") +macro(curl_dependency_option _option_name _find_name _desc_name) + set(${_option_name} "AUTO" CACHE STRING "Build curl with ${_desc_name} support (AUTO, ON or OFF)") + set_property(CACHE ${_option_name} PROPERTY STRINGS "AUTO" "ON" "OFF") - if(CURL_${_dependency} STREQUAL "AUTO") - find_package(${_dependency}) - elseif(CURL_${_dependency}) - find_package(${_dependency} REQUIRED) + if(${_option_name} STREQUAL "AUTO") + find_package(${_find_name}) + elseif(${_option_name}) + find_package(${_find_name} REQUIRED) endif() endmacro() -# Convert the passed paths to libpath linker options and add them to CMAKE_REQUIRED_LINK_OPTIONS. +# Convert the passed paths to libpath linker options and add them to CMAKE_REQUIRED_*. macro(curl_required_libpaths _libpaths_arg) - set(_libpaths "${_libpaths_arg}") - foreach(_libpath IN LISTS _libpaths) - list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "${CMAKE_LIBRARY_PATH_FLAG}${_libpath}") - endforeach() + if(CMAKE_VERSION VERSION_LESS 3.31) + set(_libpaths "${_libpaths_arg}") + foreach(_libpath IN LISTS _libpaths) + list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "${CMAKE_LIBRARY_PATH_FLAG}${_libpath}") + endforeach() + else() + list(APPEND CMAKE_REQUIRED_LINK_DIRECTORIES "${_libpaths_arg}") + endif() endmacro() diff --git a/CMake/OtherTests.cmake b/CMake/OtherTests.cmake index 62eef12..0188538 100644 --- a/CMake/OtherTests.cmake +++ b/CMake/OtherTests.cmake @@ -25,7 +25,7 @@ include(CheckCSourceCompiles) include(CheckCSourceRuns) include(CheckTypeSize) -macro(add_header_include _check _header) +macro(curl_add_header_include _check _header) if(${_check}) set(_source_epilogue "${_source_epilogue} #include <${_header}>") @@ -37,10 +37,10 @@ set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE) cmake_push_check_state() - unset(CMAKE_EXTRA_INCLUDE_FILES) + set(CMAKE_EXTRA_INCLUDE_FILES "") if(WIN32) set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") - set(CMAKE_REQUIRED_LIBRARIES "ws2_32") + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") elseif(HAVE_SYS_SOCKET_H) set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") endif() @@ -51,8 +51,8 @@ endif() if(NOT WIN32) set(_source_epilogue "#undef inline") - add_header_include(HAVE_SYS_TYPES_H "sys/types.h") - add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") + curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h") + curl_add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") check_c_source_compiles("${_source_epilogue} int main(void) { @@ -63,7 +63,7 @@ if(NOT WIN32) endif() set(_source_epilogue "#undef inline") -add_header_include(HAVE_SYS_TIME_H "sys/time.h") +curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h") check_c_source_compiles("${_source_epilogue} #include int main(void) @@ -98,9 +98,9 @@ endif() if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE) set(_source_epilogue "#undef inline") - add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") - add_header_include(HAVE_SYS_TIME_H "sys/time.h") - add_header_include(HAVE_NETDB_H "netdb.h") + curl_add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") + curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h") + curl_add_header_include(HAVE_NETDB_H "netdb.h") check_c_source_compiles("${_source_epilogue} int main(void) { @@ -141,8 +141,8 @@ endif() if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW) set(_source_epilogue "#undef inline") - add_header_include(HAVE_SYS_TYPES_H "sys/types.h") - add_header_include(HAVE_SYS_TIME_H "sys/time.h") + curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h") + curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h") check_c_source_compiles("${_source_epilogue} #include int main(void) diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index b62f97f..23341c4 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -23,17 +23,15 @@ ########################################################################### include(CheckCCompilerFlag) -unset(_picky) +set(_picky "") if(CURL_WERROR AND ((CMAKE_COMPILER_IS_GNUCC AND + NOT DOS AND # Watt-32 headers use the '#include_next' GCC extension NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0 AND NOT CMAKE_VERSION VERSION_LESS 3.23.0) OR # to avoid check_symbol_exists() conflicting with GCC -pedantic-errors CMAKE_C_COMPILER_ID MATCHES "Clang")) list(APPEND _picky "-pedantic-errors") - if(MSVC) # clang-cl - list(APPEND _picky "-Wno-language-extension-token") # Override default error to make __int64 size detection pass - endif() endif() if(APPLE AND @@ -77,7 +75,6 @@ if(PICKY_COMPILER) list(APPEND _picky_enable -Wbad-function-cast # clang 2.7 gcc 2.95 -Wconversion # clang 2.7 gcc 2.95 - -Winline # clang 1.0 gcc 1.0 -Wmissing-declarations # clang 1.0 gcc 2.7 -Wmissing-prototypes # clang 1.0 gcc 1.0 -Wnested-externs # clang 1.0 gcc 2.7 @@ -106,12 +103,11 @@ if(PICKY_COMPILER) -Wmissing-field-initializers # clang 2.7 gcc 4.1 -Wmissing-noreturn # clang 2.7 gcc 4.1 -Wno-format-nonliteral # clang 1.0 gcc 2.96 (3.0) + -Wno-sign-conversion # clang 2.9 gcc 4.3 -Wno-system-headers # clang 1.0 gcc 3.0 # -Wpadded # clang 2.9 gcc 4.1 # Not used: We cannot change public structs -Wold-style-definition # clang 2.7 gcc 3.4 -Wredundant-decls # clang 2.7 gcc 4.1 - -Wsign-conversion # clang 2.9 gcc 4.3 - -Wno-error=sign-conversion # FIXME -Wstrict-prototypes # clang 1.0 gcc 3.3 # -Wswitch-enum # clang 2.7 gcc 4.1 # Not used: It basically disallows default case -Wtype-limits # clang 2.7 gcc 4.3 @@ -121,13 +117,6 @@ if(PICKY_COMPILER) -Wvla # clang 2.8 gcc 4.3 ) - set(_picky_common - -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.3 - -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.6 g++ 11.0 - -Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0 - -Wunused-const-variable # clang 3.4 gcc 6.0 appleclang 5.1 - ) - if(CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND _picky_enable ${_picky_common_old} @@ -137,17 +126,20 @@ if(PICKY_COMPILER) ) if(NOT MSVC) list(APPEND _picky_enable - -Wlanguage-extension-token # clang 3.0 # Avoid for clang-cl to allow __int64 + -Wlanguage-extension-token # clang 3.0 ) endif() # Enable based on compiler version if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3)) list(APPEND _picky_enable - ${_picky_common} - # -Wunreachable-code-break # clang 3.5 appleclang 6.0 # Not used: Silent in "unity" builds + -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.3 + -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.6 g++ 11.0 -Wheader-guard # clang 3.4 appleclang 5.1 + -Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0 -Wsometimes-uninitialized # clang 3.2 appleclang 4.6 + # -Wunreachable-code-break # clang 3.5 appleclang 6.0 # Not used: Silent in "unity" builds + -Wunused-const-variable # clang 3.4 gcc 6.0 appleclang 5.1 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR @@ -171,9 +163,6 @@ if(PICKY_COMPILER) ) endif() else() # gcc - list(APPEND _picky_detect - ${_picky_common} - ) # Enable based on compiler version if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3) list(APPEND _picky_enable @@ -181,8 +170,8 @@ if(PICKY_COMPILER) -Wclobbered # gcc 4.3 -Wmissing-parameter-type # gcc 4.3 -Wold-style-declaration # gcc 4.3 + -Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0 -Wstrict-aliasing=3 # gcc 4.0 - -Wtrampolines # gcc 4.3 ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5 AND MINGW) @@ -192,7 +181,9 @@ if(PICKY_COMPILER) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8) list(APPEND _picky_enable + -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.3 -Wformat=2 # clang 3.0 gcc 4.8 + -Wtrampolines # gcc 4.6 ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0) @@ -207,6 +198,7 @@ if(PICKY_COMPILER) -fdelete-null-pointer-checks -Wshift-negative-value # clang 3.7 gcc 6.0 (clang default) -Wshift-overflow=2 # clang 3.0 gcc 6.0 (clang default: -Wshift-overflow) + -Wunused-const-variable # clang 3.4 gcc 6.0 appleclang 5.1 ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0) @@ -221,6 +213,7 @@ if(PICKY_COMPILER) if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0) list(APPEND _picky_enable -Warith-conversion # gcc 10.0 + -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.6 g++ 11.0 ) endif() endif() @@ -242,20 +235,41 @@ if(PICKY_COMPILER) list(APPEND _picky "${_ccopt}") endif() endforeach() + + if(CMAKE_COMPILER_IS_GNUCC) + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5) + # Avoid false positives + list(APPEND _picky "-Wno-shadow") + list(APPEND _picky "-Wno-unreachable-code") + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.2 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6) + # GCC <4.6 do not support #pragma to suppress warnings locally. Disable them globally instead. + list(APPEND _picky "-Wno-overlength-strings") + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.0 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7) + list(APPEND _picky "-Wno-missing-field-initializers") # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750 + endif() + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8) + list(APPEND _picky "-Wno-type-limits") # Avoid false positives + endif() + endif() endif() endif() # clang-cl if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND MSVC) - if(CMAKE_VERSION VERSION_LESS 3.12) - set(_picky_tmp "") - foreach(_ccopt IN LISTS _picky) - list(APPEND _picky_tmp "/clang:${_ccopt}") - endforeach() - set(_picky ${_picky_tmp}) - else() - list(TRANSFORM _picky PREPEND "/clang:") - endif() + list(APPEND _picky "-Wno-language-extension-token") # Allow __int64 + + set(_picky_tmp "") + foreach(_ccopt IN LISTS _picky) + # Prefix -Wall, otherwise clang-cl interprets it as an MSVC option and translates it to -Weverything + if(_ccopt MATCHES "^-W" AND NOT _ccopt STREQUAL "-Wall") + list(APPEND _picky_tmp ${_ccopt}) + else() + list(APPEND _picky_tmp "-clang:${_ccopt}") + endif() + endforeach() + set(_picky ${_picky_tmp}) endif() if(_picky) diff --git a/CMake/Platforms/WindowsCache.cmake b/CMake/Platforms/WindowsCache.cmake deleted file mode 100644 index 077bed2..0000000 --- a/CMake/Platforms/WindowsCache.cmake +++ /dev/null @@ -1,194 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) Daniel Stenberg, , et al. -# -# This software is licensed as described in the file COPYING, which -# you should have received as part of this distribution. The terms -# are also available at https://curl.se/docs/copyright.html. -# -# You may opt to use, copy, modify, merge, publish, distribute and/or sell -# copies of the Software, and permit persons to whom the Software is -# furnished to do so, under the terms of the COPYING file. -# -# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY -# KIND, either express or implied. -# -# SPDX-License-Identifier: curl -# -########################################################################### -if(NOT WIN32) - message(FATAL_ERROR "This file should be included on Windows platform only") -endif() - -set(HAVE_LOCALE_H 1) - -if(MINGW) - set(HAVE_SNPRINTF 1) - set(HAVE_UNISTD_H 1) - set(HAVE_LIBGEN_H 1) - set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() - set(HAVE_STDBOOL_H 1) - set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") - set(HAVE_STRTOLL 1) - set(HAVE_BASENAME 1) - set(HAVE_STRCASECMP 1) - set(HAVE_FTRUNCATE 1) - set(HAVE_SYS_PARAM_H 1) - set(HAVE_SYS_TIME_H 1) - set(HAVE_GETTIMEOFDAY 1) - set(HAVE_STRINGS_H 1) # wrapper to string.h - set(HAVE_UTIME_H 1) # wrapper to sys/utime.h - set(HAVE_DIRENT_H 1) - set(HAVE_OPENDIR 1) -else() - set(HAVE_LIBGEN_H 0) - set(HAVE_STRCASECMP 0) - set(HAVE_FTRUNCATE 0) - set(HAVE_SYS_PARAM_H 0) - set(HAVE_SYS_TIME_H 0) - set(HAVE_GETTIMEOFDAY 0) - set(HAVE_STRINGS_H 0) - set(HAVE_UTIME_H 0) - set(HAVE_DIRENT_H 0) - set(HAVE_OPENDIR 0) - if(MSVC) - set(HAVE_UNISTD_H 0) - set(HAVE_LOCALE_H 1) - set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() - set(HAVE_STDATOMIC_H 0) - if(NOT MSVC_VERSION LESS 1800) - set(HAVE_STDBOOL_H 1) - set(HAVE_STRTOLL 1) - else() - set(HAVE_STDBOOL_H 0) - set(HAVE_STRTOLL 0) - endif() - set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") - if(NOT MSVC_VERSION LESS 1900) - set(HAVE_SNPRINTF 1) - else() - set(HAVE_SNPRINTF 0) - endif() - set(HAVE_BASENAME 0) - set(HAVE_STRTOK_R 0) - set(HAVE_FILE_OFFSET_BITS 0) - set(HAVE_ATOMIC 0) - endif() -endif() - -# Available in Windows XP and newer -set(HAVE_GETADDRINFO 1) -set(HAVE_FREEADDRINFO 1) - -set(HAVE_FCHMOD 0) -set(HAVE_SOCKETPAIR 0) -set(HAVE_SENDMSG 0) -set(HAVE_SENDMMSG 0) -set(HAVE_ALARM 0) -set(HAVE_FCNTL 0) -set(HAVE_GETPPID 0) -set(HAVE_UTIMES 0) -set(HAVE_GETPWUID_R 0) -set(HAVE_STRERROR_R 0) -set(HAVE_SIGINTERRUPT 0) -set(HAVE_PIPE 0) -set(HAVE_EVENTFD 0) -set(HAVE_IF_NAMETOINDEX 0) -set(HAVE_GETRLIMIT 0) -set(HAVE_SETRLIMIT 0) -set(HAVE_FSETXATTR 0) -set(HAVE_SETLOCALE 1) -set(HAVE_SETMODE 1) -set(HAVE__SETMODE 1) -set(HAVE_GETPEERNAME 1) -set(HAVE_GETSOCKNAME 1) -set(HAVE_GETHOSTNAME 1) - -set(HAVE_RECV 1) -set(HAVE_SEND 1) -set(HAVE_STROPTS_H 0) -set(HAVE_SYS_XATTR_H 0) -set(HAVE_ARC4RANDOM 0) -set(HAVE_FNMATCH 0) -set(HAVE_SCHED_YIELD 0) -set(HAVE_ARPA_INET_H 0) -set(HAVE_FCNTL_H 1) -set(HAVE_IFADDRS_H 0) -set(HAVE_IO_H 1) -set(HAVE_NETDB_H 0) -set(HAVE_NETINET_IN_H 0) -set(HAVE_NETINET_IN6_H 0) -set(HAVE_NETINET_TCP_H 0) -set(HAVE_NETINET_UDP_H 0) -set(HAVE_NET_IF_H 0) -set(HAVE_IOCTL_SIOCGIFADDR 0) -set(HAVE_POLL_H 0) -set(HAVE_POLL 0) -set(HAVE_PWD_H 0) -set(HAVE_SYS_EVENTFD_H 0) -set(HAVE_SYS_FILIO_H 0) -set(HAVE_SYS_WAIT_H 0) -set(HAVE_SYS_IOCTL_H 0) -set(HAVE_SYS_POLL_H 0) -set(HAVE_SYS_RESOURCE_H 0) -set(HAVE_SYS_SELECT_H 0) -set(HAVE_SYS_SOCKET_H 0) -set(HAVE_SYS_SOCKIO_H 0) -set(HAVE_SYS_STAT_H 1) -set(HAVE_SYS_TYPES_H 1) -set(HAVE_SYS_UN_H 0) -set(HAVE_SYS_UTIME_H 1) -set(HAVE_TERMIOS_H 0) -set(HAVE_TERMIO_H 0) -set(HAVE_LINUX_TCP_H 0) - -set(HAVE_FSEEKO 0) # mingw-w64 2.0.0 and newer has it -set(HAVE_SOCKET 1) -set(HAVE_SELECT 1) -set(HAVE_STRDUP 1) -set(HAVE_STRICMP 1) -set(HAVE_STRCMPI 1) -set(HAVE_MEMRCHR 0) -set(HAVE_CLOSESOCKET 1) -set(HAVE_SIGSETJMP 0) -set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) -set(HAVE_GETPASS_R 0) -set(HAVE_GETPWUID 0) -set(HAVE_GETEUID 0) -set(HAVE_UTIME 1) -set(HAVE_GMTIME_R 0) -set(HAVE_GETHOSTBYNAME_R 0) -set(HAVE_SIGNAL 1) -set(HAVE_SIGACTION 0) -set(HAVE_GLIBC_STRERROR_R 0) -set(HAVE_GETIFADDRS 0) -set(HAVE_FCNTL_O_NONBLOCK 0) -set(HAVE_IOCTLSOCKET 1) -set(HAVE_IOCTLSOCKET_CAMEL 0) -set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0) -set(HAVE_IOCTLSOCKET_FIONBIO 1) -set(HAVE_IOCTL_FIONBIO 0) -set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) -set(HAVE_POSIX_STRERROR_R 0) -set(HAVE_MSG_NOSIGNAL 0) -set(HAVE_STRUCT_TIMEVAL 1) -set(HAVE_STRUCT_SOCKADDR_STORAGE 1) - -set(HAVE_GETHOSTBYNAME_R_3 0) -set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0) -set(HAVE_GETHOSTBYNAME_R_5 0) -set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0) -set(HAVE_GETHOSTBYNAME_R_6 0) -set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0) - -set(HAVE_O_NONBLOCK 0) -set(HAVE_IN_ADDR_T 0) -set(STDC_HEADERS 1) - -set(HAVE_SIZEOF_SUSECONDS_T 0) -set(HAVE_SIZEOF_SA_FAMILY_T 0) diff --git a/CMake/Utilities.cmake b/CMake/Utilities.cmake index 0ecfa31..869d591 100644 --- a/CMake/Utilities.cmake +++ b/CMake/Utilities.cmake @@ -23,13 +23,31 @@ ########################################################################### # File containing various utilities -# Returns number of arguments that evaluate to true -function(count_true _output_count_var) - set(lst_len 0) - foreach(option_var IN LISTS ARGN) - if(${option_var}) - math(EXPR lst_len "${lst_len} + 1") +# Return number of arguments that evaluate to true +function(curl_count_true _output_count_var) + set(_list_len 0) + foreach(_option_var IN LISTS ARGN) + if(${_option_var}) + math(EXPR _list_len "${_list_len} + 1") endif() endforeach() - set(${_output_count_var} ${lst_len} PARENT_SCOPE) + set(${_output_count_var} ${_list_len} PARENT_SCOPE) +endfunction() + +# Dump all defined variables with their values +function(curl_dumpvars) + message("::group::CMake Variable Dump") + get_cmake_property(_vars VARIABLES) + foreach(_var IN ITEMS ${_vars}) + get_property(_var_type CACHE ${_var} PROPERTY TYPE) + get_property(_var_advanced CACHE ${_var} PROPERTY ADVANCED) + if(_var_type) + set(_var_type ":${_var_type}") + endif() + if(_var_advanced) + set(_var_advanced " [adv]") + endif() + message("${_var}${_var_type}${_var_advanced} = ${${_var}}") + endforeach() + message("::endgroup::") endfunction() diff --git a/CMake/curl-config.cmake.in b/CMake/curl-config.cmake.in index aa9eb51..a4df052 100644 --- a/CMake/curl-config.cmake.in +++ b/CMake/curl-config.cmake.in @@ -23,13 +23,6 @@ ########################################################################### @PACKAGE_INIT@ -if(UNIX OR VCPKG_TOOLCHAIN OR (MINGW AND NOT CMAKE_CROSSCOMPILING)) # Keep in sync with root CMakeLists.txt - set(_curl_use_pkgconfig_default ON) -else() - set(_curl_use_pkgconfig_default OFF) -endif() -option(CURL_USE_PKGCONFIG "Enable pkg-config to detect @PROJECT_NAME@ dependencies" ${_curl_use_pkgconfig_default}) - include(CMakeFindDependencyMacro) if("@USE_OPENSSL@") find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@") @@ -39,7 +32,6 @@ if("@HAVE_LIBZ@") endif() include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") -check_required_components("@PROJECT_NAME@") # Alias for either shared or static library if(NOT TARGET @PROJECT_NAME@::libcurl) @@ -47,5 +39,32 @@ if(NOT TARGET @PROJECT_NAME@::libcurl) endif() # For compatibility with CMake's FindCURL.cmake +set(CURL_VERSION_STRING "@CURLVERSION@") set(CURL_LIBRARIES @PROJECT_NAME@::libcurl) set_and_check(CURL_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") + +set(CURL_SUPPORTED_PROTOCOLS "@CURL_SUPPORTED_PROTOCOLS_LIST@") +set(CURL_SUPPORTED_FEATURES "@CURL_SUPPORTED_FEATURES_LIST@") + +foreach(_item IN LISTS CURL_SUPPORTED_PROTOCOLS CURL_SUPPORTED_FEATURES) + set(CURL_SUPPORTS_${_item} TRUE) +endforeach() + +set(_missing_req "") +foreach(_item IN LISTS CURL_FIND_COMPONENTS) + if(CURL_SUPPORTS_${_item}) + set(CURL_${_item}_FOUND TRUE) + elseif(CURL_FIND_REQUIRED_${_item}) + list(APPEND _missing_req ${_item}) + endif() +endforeach() + +if(_missing_req) + string(REPLACE ";" " " _missing_req "${_missing_req}") + if(CURL_FIND_REQUIRED) + message(FATAL_ERROR "CURL: missing required components: ${_missing_req}") + endif() + unset(_missing_req) +endif() + +check_required_components("@PROJECT_NAME@") diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake new file mode 100644 index 0000000..9d46736 --- /dev/null +++ b/CMake/win32-cache.cmake @@ -0,0 +1,205 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +if(NOT WIN32) + message(FATAL_ERROR "This file should be included on Windows platform only") +endif() + +set(HAVE_LOCALE_H 1) + +if(MINGW) + set(HAVE_SNPRINTF 1) + set(HAVE_UNISTD_H 1) + set(HAVE_LIBGEN_H 1) + set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() + set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() + set(HAVE_STDBOOL_H 1) + set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") + set(HAVE_STRTOLL 1) + set(HAVE_BASENAME 1) + set(HAVE_FTRUNCATE 1) + set(HAVE_SYS_PARAM_H 1) + set(HAVE_SYS_TIME_H 1) + set(HAVE_GETTIMEOFDAY 1) + set(HAVE_STRINGS_H 1) # wrapper to string.h + set(HAVE_UTIME_H 1) # wrapper to sys/utime.h + set(HAVE_DIRENT_H 1) + set(HAVE_OPENDIR 1) + if(MINGW64_VERSION) + if(NOT MINGW64_VERSION VERSION_LESS 4.0) + set(HAVE_STRTOK_R 1) + else() + set(HAVE_STRTOK_R 0) + endif() + endif() + if((CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.9) OR + (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6)) + set(HAVE_STDATOMIC_H 1) + set(HAVE_ATOMIC 1) + else() + set(HAVE_STDATOMIC_H 0) + set(HAVE_ATOMIC 0) + endif() +else() + set(HAVE_LIBGEN_H 0) + set(HAVE_FTRUNCATE 0) + set(HAVE_SYS_PARAM_H 0) + set(HAVE_SYS_TIME_H 0) + set(HAVE_GETTIMEOFDAY 0) + set(HAVE_STRINGS_H 0) + set(HAVE_UTIME_H 0) + set(HAVE_DIRENT_H 0) + set(HAVE_OPENDIR 0) + if(MSVC) + set(HAVE_UNISTD_H 0) + set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() + set(HAVE_STDATOMIC_H 0) + if(NOT MSVC_VERSION LESS 1600) + set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() + else() + set(HAVE_STDINT_H 0) # detected by CMake internally in check_type_size() + endif() + if(NOT MSVC_VERSION LESS 1800) + set(HAVE_STDBOOL_H 1) + set(HAVE_STRTOLL 1) + else() + set(HAVE_STDBOOL_H 0) + set(HAVE_STRTOLL 0) + endif() + set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") + if(NOT MSVC_VERSION LESS 1900) + set(HAVE_SNPRINTF 1) + else() + set(HAVE_SNPRINTF 0) + endif() + set(HAVE_BASENAME 0) + set(HAVE_STRTOK_R 0) + set(HAVE_FILE_OFFSET_BITS 0) + set(HAVE_ATOMIC 0) + endif() +endif() + +# Available in Windows XP and newer +set(HAVE_GETADDRINFO 1) +set(HAVE_FREEADDRINFO 1) + +set(HAVE_SOCKETPAIR 0) +set(HAVE_SENDMSG 0) +set(HAVE_SENDMMSG 0) +set(HAVE_ALARM 0) +set(HAVE_FCNTL 0) +set(HAVE_GETPPID 0) +set(HAVE_UTIMES 0) +set(HAVE_GETPWUID_R 0) +set(HAVE_STRERROR_R 0) +set(HAVE_SIGINTERRUPT 0) +set(HAVE_PIPE 0) +set(HAVE_EVENTFD 0) +set(HAVE_IF_NAMETOINDEX 0) +set(HAVE_GETRLIMIT 0) +set(HAVE_SETRLIMIT 0) +set(HAVE_FSETXATTR 0) +set(HAVE_SETLOCALE 1) +set(HAVE_SETMODE 1) +set(HAVE__SETMODE 1) +set(HAVE_GETPEERNAME 1) +set(HAVE_GETSOCKNAME 1) +set(HAVE_GETHOSTNAME 1) + +set(HAVE_RECV 1) +set(HAVE_SEND 1) +set(HAVE_STROPTS_H 0) +set(HAVE_ARC4RANDOM 0) +set(HAVE_FNMATCH 0) +set(HAVE_ARPA_INET_H 0) +set(HAVE_FCNTL_H 1) +set(HAVE_IFADDRS_H 0) +set(HAVE_IO_H 1) +set(HAVE_NETDB_H 0) +set(HAVE_NETINET_IN_H 0) +set(HAVE_NETINET_IN6_H 0) +set(HAVE_NETINET_TCP_H 0) +set(HAVE_NETINET_UDP_H 0) +set(HAVE_NET_IF_H 0) +set(HAVE_IOCTL_SIOCGIFADDR 0) +set(HAVE_POLL_H 0) +set(HAVE_POLL 0) +set(HAVE_PWD_H 0) +set(HAVE_SYS_EVENTFD_H 0) +set(HAVE_SYS_FILIO_H 0) +set(HAVE_SYS_IOCTL_H 0) +set(HAVE_SYS_POLL_H 0) +set(HAVE_SYS_RESOURCE_H 0) +set(HAVE_SYS_SELECT_H 0) +set(HAVE_SYS_SOCKET_H 0) +set(HAVE_SYS_SOCKIO_H 0) +set(HAVE_SYS_STAT_H 1) +set(HAVE_SYS_TYPES_H 1) +set(HAVE_SYS_UN_H 0) +set(HAVE_SYS_UTIME_H 1) +set(HAVE_TERMIOS_H 0) +set(HAVE_TERMIO_H 0) +set(HAVE_LINUX_TCP_H 0) + +set(HAVE_SOCKET 1) +set(HAVE_SELECT 1) +set(HAVE_STRDUP 1) +set(HAVE_MEMRCHR 0) +set(HAVE_CLOSESOCKET 1) +set(HAVE_SIGSETJMP 0) +set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) +set(HAVE_GETPASS_R 0) +set(HAVE_GETPWUID 0) +set(HAVE_GETEUID 0) +set(HAVE_UTIME 1) +set(HAVE_GMTIME_R 0) +set(HAVE_GETHOSTBYNAME_R 0) +set(HAVE_SIGNAL 1) +set(HAVE_SIGACTION 0) +set(HAVE_GLIBC_STRERROR_R 0) +set(HAVE_GETIFADDRS 0) +set(HAVE_FCNTL_O_NONBLOCK 0) +set(HAVE_IOCTLSOCKET 1) +set(HAVE_IOCTLSOCKET_CAMEL 0) +set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0) +set(HAVE_IOCTLSOCKET_FIONBIO 1) +set(HAVE_IOCTL_FIONBIO 0) +set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) +set(HAVE_POSIX_STRERROR_R 0) +set(HAVE_MSG_NOSIGNAL 0) +set(HAVE_STRUCT_TIMEVAL 1) +set(HAVE_STRUCT_SOCKADDR_STORAGE 1) +set(HAVE_TIME_T_UNSIGNED 0) + +set(HAVE_GETHOSTBYNAME_R_3 0) +set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0) +set(HAVE_GETHOSTBYNAME_R_5 0) +set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0) +set(HAVE_GETHOSTBYNAME_R_6 0) +set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0) + +set(HAVE_IN_ADDR_T 0) +set(STDC_HEADERS 1) + +set(HAVE_SIZEOF_SUSECONDS_T 0) +set(HAVE_SIZEOF_SA_FAMILY_T 0) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cba6f6..30c0154 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ # HAVE_SSL_SET0_WBIO: `SSL_set0_wbio` present in OpenSSL/wolfSSL # HAVE_OPENSSL_SRP: `SSL_CTX_set_srp_username` present in OpenSSL/wolfSSL # HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS -# HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL +# HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT: `SSL_set_quic_use_legacy_codepoint` present in OpenSSL/wolfSSL # HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in quiche # HAVE_ECH: ECH API checks for OpenSSL, BoringSSL or wolfSSL # @@ -66,15 +66,6 @@ if(NOT "$ENV{CURL_BUILDINFO}$ENV{CURL_CI}$ENV{CI}" STREQUAL "") endforeach() endif() -function(curl_dumpvars) # Dump all defined variables with their values - message("::group::CMake Variable Dump") - get_cmake_property(_vars VARIABLES) - foreach(_var ${_vars}) - message("${_var} = ${${_var}}") - endforeach() - message("::endgroup::") -endfunction() - set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) include(Macros) @@ -95,7 +86,24 @@ project(CURL VERSION "${_curl_version_sem}" LANGUAGES C) -unset(_target_flags) +# CMake does not recognize some targets accurately. Touch up configuration manually as a workaround. +if(WINDOWS_STORE AND MINGW) # mingw UWP build + # CMake (as of v3.31.2) gets confused and applies the MSVC rc.exe command-line + # template to windres. Reset it to the windres template via 'Modules/Platform/Windows-windres.cmake': + set(CMAKE_RC_COMPILE_OBJECT " -O coff ") +elseif(DOS AND CMAKE_COMPILER_IS_GNUCC) # DJGPP + set(CMAKE_STATIC_LIBRARY_PREFIX "lib") + set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") + set(CMAKE_FIND_LIBRARY_PREFIXES "lib") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") +endif() + +# Fill platform level variable when using CMake's built-in Android configuration +if(ANDROID AND NOT DEFINED ANDROID_PLATFORM_LEVEL AND NOT CMAKE_SYSTEM_VERSION EQUAL 1) + set(ANDROID_PLATFORM_LEVEL "${CMAKE_SYSTEM_VERSION}") +endif() + +set(_target_flags "") if(APPLE) set(_target_flags "${_target_flags} APPLE") endif() @@ -105,6 +113,9 @@ endif() if(BSD) set(_target_flags "${_target_flags} BSD") endif() +if(ANDROID) + set(_target_flags "${_target_flags} ANDROID-${ANDROID_PLATFORM_LEVEL}") +endif() if(WIN32) set(_target_flags "${_target_flags} WIN32") endif() @@ -117,6 +128,12 @@ endif() if(MSYS) set(_target_flags "${_target_flags} MSYS") endif() +if(DOS) + set(_target_flags "${_target_flags} DOS") +endif() +if(AMIGA) + set(_target_flags "${_target_flags} AMIGA") +endif() if(CMAKE_COMPILER_IS_GNUCC) set(_target_flags "${_target_flags} GCC") endif() @@ -124,7 +141,7 @@ if(MINGW) set(_target_flags "${_target_flags} MINGW") endif() if(MSVC) - set(_target_flags "${_target_flags} MSVC") + set(_target_flags "${_target_flags} MSVC-${MSVC_VERSION}") endif() if(VCPKG_TOOLCHAIN) set(_target_flags "${_target_flags} VCPKG") @@ -176,8 +193,8 @@ if(WIN32) option(CURL_STATIC_CRT "Build libcurl with static CRT with MSVC (/MT)" OFF) if(CURL_STATIC_CRT AND MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -MT") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -MTd") endif() option(ENABLE_UNICODE "Use the Unicode version of the Windows API functions" OFF) @@ -202,24 +219,43 @@ if(WIN32) # Detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT curl_internal_test(HAVE_WIN32_WINNT) if(HAVE_WIN32_WINNT) - string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") - string(REGEX REPLACE ".*_WIN32_WINNT=" "" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") + string(REGEX MATCH "_WIN32_WINNT=0x[0-9a-fA-F]+" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") + string(REGEX REPLACE "_WIN32_WINNT=" "" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") # pad to 4 digits string(TOLOWER "${CURL_TEST_OUTPUT}" HAVE_WIN32_WINNT) message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}") endif() - # Avoid storing HAVE_WIN32_WINNT in CMake cache - unset(HAVE_WIN32_WINNT CACHE) + unset(HAVE_WIN32_WINNT CACHE) # Avoid storing in CMake cache + + if(MINGW) + # Detect __MINGW64_VERSION_MAJOR, __MINGW64_VERSION_MINOR and store as MINGW64_VERSION + curl_internal_test(MINGW64_VERSION) + if(MINGW64_VERSION) + string(REGEX MATCH "MINGW64_VERSION=[0-9]+\.[0-9]+" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") + string(REGEX REPLACE "MINGW64_VERSION=" "" MINGW64_VERSION "${CURL_TEST_OUTPUT}") + message(STATUS "Found MINGW64_VERSION=${MINGW64_VERSION}") + endif() + unset(MINGW64_VERSION CACHE) # Avoid storing in CMake cache + endif() +elseif(DOS OR AMIGA) + set(BUILD_SHARED_LIBS OFF) + set(BUILD_STATIC_LIBS ON) endif() option(CURL_LTO "Enable compiler Link Time Optimizations" OFF) -cmake_dependent_option(ENABLE_THREADED_RESOLVER "Enable threaded DNS lookup" - ON "NOT ENABLE_ARES" - OFF) +if(NOT DOS AND NOT AMIGA) + # if c-ares is used, default the threaded resolver to OFF + if(ENABLE_ARES) + set(_enable_threaded_resolver_default OFF) + else() + set(_enable_threaded_resolver_default ON) + endif() + option(ENABLE_THREADED_RESOLVER "Enable threaded DNS lookup" ${_enable_threaded_resolver_default}) +endif() include(PickyWarnings) -if(CMAKE_SYSTEM_NAME MATCHES "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE") # Required for sendmmsg() endif() @@ -241,6 +277,25 @@ if(ENABLE_CURLDEBUG) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURLDEBUG") endif() +option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF) +if(CURL_CLANG_TIDY) + set(CMAKE_UNITY_BUILD OFF) + set(_tidy_checks "") + list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.strcpy") + list(APPEND _tidy_checks "-clang-analyzer-optin.performance.Padding") + list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling") + string(REPLACE ";" "," _tidy_checks "${_tidy_checks}") + find_program(CLANG_TIDY NAMES "clang-tidy" REQUIRED) + set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY}" "-checks=${_tidy_checks}" "-quiet") + unset(_tidy_checks) + if(CURL_WERROR) + list(APPEND CMAKE_C_CLANG_TIDY "--warnings-as-errors=*") + endif() + if(CURL_CLANG_TIDYFLAGS) + list(APPEND CMAKE_C_CLANG_TIDY ${CURL_CLANG_TIDYFLAGS}) + endif() +endif() + # For debug libs and exes, add "-d" postfix if(NOT DEFINED CMAKE_DEBUG_POSTFIX) set(CMAKE_DEBUG_POSTFIX "-d") @@ -273,7 +328,9 @@ else() endif() # Override to force-disable or force-enable the use of pkg-config. -if(UNIX OR VCPKG_TOOLCHAIN OR (MINGW AND NOT CMAKE_CROSSCOMPILING)) # Keep in sync with CMake/curl-config.cmake.in +if((UNIX AND NOT ANDROID AND (NOT APPLE OR CMAKE_SYSTEM_NAME MATCHES "Darwin")) OR + VCPKG_TOOLCHAIN OR + (MINGW AND NOT CMAKE_CROSSCOMPILING)) set(_curl_use_pkgconfig_default ON) else() set(_curl_use_pkgconfig_default OFF) @@ -289,8 +346,12 @@ if(ENABLE_ARES) set(USE_ARES 1) find_package(Cares REQUIRED) list(APPEND CURL_LIBS ${CARES_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libcares") - add_definitions("-DCARES_NO_DEPRECATED") # Ignore c-ares deprecation warnings + list(APPEND CURL_LIBDIRS ${CARES_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${CARES_PC_REQUIRES}) + link_directories(${CARES_LIBRARY_DIRS}) + if(CARES_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CARES_CFLAGS}") + endif() endif() include(CurlSymbolHiding) @@ -429,7 +490,9 @@ if(ENABLE_IPV6 AND NOT WIN32) check_struct_has_member("struct sockaddr_in6" "sin6_addr" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_ADDR) check_struct_has_member("struct sockaddr_in6" "sin6_scope_id" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if(NOT HAVE_SOCKADDR_IN6_SIN6_ADDR) - message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support") + if(NOT DOS AND NOT AMIGA) + message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support") + endif() # Force the feature off as this name is used as guard macro... set(ENABLE_IPV6 OFF CACHE BOOL "Enable IPv6 support" FORCE) endif() @@ -437,12 +500,11 @@ if(ENABLE_IPV6 AND NOT WIN32) if(APPLE AND NOT ENABLE_ARES) set(_use_core_foundation_and_core_services ON) - find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration") + find_library(SYSTEMCONFIGURATION_FRAMEWORK NAMES "SystemConfiguration") mark_as_advanced(SYSTEMCONFIGURATION_FRAMEWORK) if(NOT SYSTEMCONFIGURATION_FRAMEWORK) message(FATAL_ERROR "SystemConfiguration framework not found") endif() - list(APPEND CURL_LIBS "-framework SystemConfiguration") endif() endif() @@ -452,6 +514,19 @@ endif() find_package(Perl) +if(PERL_EXECUTABLE) + add_custom_target(curl-ca-bundle + COMMENT "Generating a fresh ca-bundle.crt" VERBATIM USES_TERMINAL + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl" -b -l -u "lib/ca-bundle.crt" + DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl" + ) + add_custom_target(curl-ca-firefox + COMMENT "generating a fresh ca-bundle.crt" VERBATIM USES_TERMINAL + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" "lib/ca-bundle.crt" + DEPENDS "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" + ) +endif() + option(BUILD_LIBCURL_DOCS "Build libcurl man pages" ON) option(BUILD_MISC_DOCS "Build misc man pages (e.g. curl-config and mk-ca-bundle)" ON) option(ENABLE_CURL_MANUAL "Build the man page for curl and enable its -M/--manual option" ON) @@ -478,6 +553,9 @@ endif() # If we are on Haiku, make sure that the network library is brought in. if(CMAKE_SYSTEM_NAME STREQUAL "Haiku") list(APPEND CURL_LIBS "network") +elseif(AMIGA) + list(APPEND CURL_LIBS "net" "m" "atomic") + list(APPEND CMAKE_REQUIRED_LIBRARIES "net" "m" "atomic") endif() # Include all the necessary files for macros @@ -490,14 +568,19 @@ include(CheckSymbolExists) include(CheckTypeSize) include(CheckCSourceCompiles) -# Preload settings on Windows if(WIN32) - include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake") + # Preload settings on Windows + include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/win32-cache.cmake") elseif(APPLE) # Fast-track predictable feature detections set(HAVE_EVENTFD 0) set(HAVE_GETPASS_R 0) set(HAVE_SENDMMSG 0) +elseif(AMIGA) + set(HAVE_GETADDRINFO 0) # Breaks the build when detected and used. +endif() +if(DOS OR AMIGA) + set(HAVE_TIME_T_UNSIGNED 1) endif() if(ENABLE_THREADED_RESOLVER) @@ -512,15 +595,34 @@ if(ENABLE_THREADED_RESOLVER) endif() # Check for all needed libraries -if(NOT WIN32 AND NOT APPLE) +if(DOS) + if(WATT_ROOT) + set(USE_WATT32 ON) + # FIXME upstream: must specify the full path to avoid CMake converting "watt" to "watt.lib" + list(APPEND CURL_LIBS "${WATT_ROOT}/lib/libwatt.a") + include_directories(SYSTEM "${WATT_ROOT}/inc") + list(APPEND CMAKE_REQUIRED_INCLUDES "${WATT_ROOT}/inc") + else() + message(FATAL_ERROR "Set WATT_ROOT variable to the root installation of Watt-32.") + endif() +elseif(AMIGA) + if(AMISSL_INCLUDE_DIR AND AMISSL_STUBS_LIBRARY AND AMISSL_AUTO_LIBRARY) + set(USE_AMISSL ON) + list(APPEND CMAKE_REQUIRED_INCLUDES "${AMISSL_INCLUDE_DIR}") + list(APPEND CMAKE_REQUIRED_LIBRARIES "${AMISSL_STUBS_LIBRARY}" "${AMISSL_AUTO_LIBRARY}") + set(OPENSSL_INCLUDE_DIR "${AMISSL_INCLUDE_DIR}") + set(OPENSSL_SSL_LIBRARY "${AMISSL_STUBS_LIBRARY}") + set(OPENSSL_CRYPTO_LIBRARY "${AMISSL_AUTO_LIBRARY}") + set(CURL_USE_OPENSSL ON) + set(CURL_CA_FALLBACK ON CACHE BOOL "") + endif() +elseif(NOT WIN32 AND NOT APPLE) check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) if(HAVE_LIBSOCKET) set(CURL_LIBS "socket;${CURL_LIBS}") endif() endif() -check_function_exists("gethostname" HAVE_GETHOSTNAME) - if(WIN32) list(APPEND CURL_LIBS "ws2_32" "bcrypt") endif() @@ -565,7 +667,7 @@ if(USE_OPENSSL_QUIC AND NOT CURL_USE_OPENSSL) endif() option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF) -count_true(_enabled_ssl_options_count +curl_count_true(_enabled_ssl_options_count CURL_USE_SCHANNEL CURL_USE_SECTRANSP CURL_USE_OPENSSL @@ -597,15 +699,15 @@ endif() if(CURL_USE_SECTRANSP) set(_use_core_foundation_and_core_services ON) - find_library(SECURITY_FRAMEWORK "Security") + find_library(SECURITY_FRAMEWORK NAMES "Security") mark_as_advanced(SECURITY_FRAMEWORK) if(NOT SECURITY_FRAMEWORK) message(FATAL_ERROR "Security framework not found") endif() + list(APPEND CURL_LIBS "-framework Security") set(_ssl_enabled ON) set(USE_SECTRANSP ON) - list(APPEND CURL_LIBS "-framework Security") if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "secure-transport") set(_valid_default_ssl_backend TRUE) @@ -615,19 +717,19 @@ if(CURL_USE_SECTRANSP) endif() if(_use_core_foundation_and_core_services) - find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation") + find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation") mark_as_advanced(COREFOUNDATION_FRAMEWORK) - find_library(CORESERVICES_FRAMEWORK "CoreServices") - mark_as_advanced(CORESERVICES_FRAMEWORK) - if(NOT COREFOUNDATION_FRAMEWORK) message(FATAL_ERROR "CoreFoundation framework not found") endif() + list(APPEND CURL_LIBS "-framework CoreFoundation") + + find_library(CORESERVICES_FRAMEWORK NAMES "CoreServices") + mark_as_advanced(CORESERVICES_FRAMEWORK) if(NOT CORESERVICES_FRAMEWORK) message(FATAL_ERROR "CoreServices framework not found") endif() - - list(APPEND CURL_LIBS "-framework CoreFoundation" "-framework CoreServices") + list(APPEND CURL_LIBS "-framework CoreServices") endif() if(CURL_USE_OPENSSL) @@ -646,14 +748,39 @@ if(CURL_USE_OPENSSL) set(_curl_ca_bundle_supported TRUE) cmake_push_check_state() - set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + list(APPEND CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) if(NOT DEFINED HAVE_BORINGSSL) check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) endif() if(NOT DEFINED HAVE_AWSLC) check_symbol_exists("OPENSSL_IS_AWSLC" "openssl/base.h" HAVE_AWSLC) endif() + if(NOT DEFINED HAVE_LIBRESSL) + check_symbol_exists("LIBRESSL_VERSION_NUMBER" "openssl/opensslv.h" HAVE_LIBRESSL) + endif() cmake_pop_check_state() + + if(HAVE_BORINGSSL OR HAVE_AWSLC) + if(OPENSSL_USE_STATIC_LIBS AND CMAKE_C_COMPILER_ID MATCHES "Clang") + list(APPEND CURL_LIBS "stdc++") + list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++") + endif() + endif() + + if(HAVE_BORINGSSL) + set(_openssl "BoringSSL") + elseif(HAVE_AWSLC) + set(_openssl "AWS-LC") + elseif(HAVE_LIBRESSL) + set(_openssl "LibreSSL") + elseif(USE_AMISSL) + set(_openssl "AmiSSL") + else() + set(_openssl "OpenSSL") + if(OPENSSL_VERSION VERSION_LESS 1.1.1) + message(WARNING "OpenSSL ${OPENSSL_VERSION} does not support TLS 1.3.") + endif() + endif() endif() if(CURL_USE_MBEDTLS) @@ -696,7 +823,7 @@ if(CURL_USE_WOLFSSL) set(USE_WOLFSSL ON) list(APPEND CURL_LIBS ${WOLFSSL_LIBRARIES}) list(APPEND CURL_LIBDIRS ${WOLFSSL_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "wolfssl") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${WOLFSSL_PC_REQUIRES}) include_directories(SYSTEM ${WOLFSSL_INCLUDE_DIRS}) link_directories(${WOLFSSL_LIBRARY_DIRS}) if(WOLFSSL_CFLAGS) @@ -725,7 +852,7 @@ if(CURL_USE_GNUTLS) set(USE_GNUTLS ON) list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} ${NETTLE_LIBRARIES}) list(APPEND CURL_LIBDIRS ${NETTLE_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls" "nettle") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls" ${NETTLE_PC_REQUIRES}) include_directories(SYSTEM ${GNUTLS_INCLUDE_DIRS} ${NETTLE_INCLUDE_DIRS}) link_directories(${NETTLE_LIBRARY_DIRS}) if(NETTLE_CFLAGS) @@ -739,8 +866,8 @@ if(CURL_USE_GNUTLS) if(NOT DEFINED HAVE_GNUTLS_SRP AND NOT CURL_DISABLE_SRP) cmake_push_check_state() - set(CMAKE_REQUIRED_INCLUDES ${GNUTLS_INCLUDE_DIRS}) - set(CMAKE_REQUIRED_LIBRARIES ${GNUTLS_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_INCLUDES ${GNUTLS_INCLUDE_DIRS}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${GNUTLS_LIBRARIES}) check_symbol_exists("gnutls_srp_verifier" "gnutls/gnutls.h" HAVE_GNUTLS_SRP) cmake_pop_check_state() endif() @@ -770,51 +897,56 @@ if(CURL_DEFAULT_SSL_BACKEND AND NOT _valid_default_ssl_backend) endif() # Keep ZLIB detection after TLS detection, -# and before calling openssl_check_symbol_exists(). +# and before calling curl_openssl_check_symbol_exists(). set(HAVE_LIBZ OFF) -curl_dependency_option(ZLIB) +curl_dependency_option(CURL_ZLIB ZLIB "ZLIB") if(ZLIB_FOUND) set(HAVE_LIBZ ON) - # Depend on ZLIB via imported targets. This allows our dependents to # get our dependencies transitively. list(APPEND CURL_LIBS ZLIB::ZLIB) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "zlib") endif() -option(CURL_BROTLI "Use brotli" OFF) set(HAVE_BROTLI OFF) -if(CURL_BROTLI) - find_package(Brotli REQUIRED) - if(BROTLI_FOUND) - set(HAVE_BROTLI ON) - list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libbrotlidec") - include_directories(SYSTEM ${BROTLI_INCLUDE_DIRS}) +curl_dependency_option(CURL_BROTLI Brotli "brotli") +if(BROTLI_FOUND) + set(HAVE_BROTLI ON) + list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${BROTLI_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${BROTLI_PC_REQUIRES}) + include_directories(SYSTEM ${BROTLI_INCLUDE_DIRS}) + link_directories(${BROTLI_LIBRARY_DIRS}) + if(BROTLI_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${BROTLI_CFLAGS}") endif() endif() -option(CURL_ZSTD "Use zstd" OFF) set(HAVE_ZSTD OFF) -if(CURL_ZSTD) - find_package(Zstd REQUIRED) - if(ZSTD_FOUND AND NOT ZSTD_VERSION VERSION_LESS 1.0.0) +curl_dependency_option(CURL_ZSTD Zstd "zstd") +if(ZSTD_FOUND) + if(NOT ZSTD_VERSION VERSION_LESS 1.0.0) set(HAVE_ZSTD ON) list(APPEND CURL_LIBS ${ZSTD_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libzstd") + list(APPEND CURL_LIBDIRS ${ZSTD_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${ZSTD_PC_REQUIRES}) include_directories(SYSTEM ${ZSTD_INCLUDE_DIRS}) + link_directories(${ZSTD_LIBRARY_DIRS}) + if(ZSTD_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZSTD_CFLAGS}") + endif() else() message(WARNING "zstd v1.0.0 or newer is required, disabling zstd support.") endif() endif() -# Check symbol in an OpenSSL-like TLS backend, or in _extra_libs depending on it. -macro(openssl_check_symbol_exists _symbol _files _variable _extra_libs) +# Check symbol in an OpenSSL-like TLS backend. +macro(curl_openssl_check_symbol_exists _symbol _files _variable) cmake_push_check_state() if(USE_OPENSSL) - set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") + list(APPEND CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + list(APPEND CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") if(HAVE_LIBZ) list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") endif() @@ -822,9 +954,10 @@ macro(openssl_check_symbol_exists _symbol _files _variable _extra_libs) list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL endif() - elseif(USE_WOLFSSL) - set(CMAKE_REQUIRED_INCLUDES "${WOLFSSL_INCLUDE_DIRS}") - set(CMAKE_REQUIRED_LIBRARIES "${WOLFSSL_LIBRARIES}") + endif() + if(USE_WOLFSSL) + list(APPEND CMAKE_REQUIRED_INCLUDES "${WOLFSSL_INCLUDE_DIRS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES "${WOLFSSL_LIBRARIES}") curl_required_libpaths("${WOLFSSL_LIBRARY_DIRS}") if(HAVE_LIBZ) list(APPEND CMAKE_REQUIRED_INCLUDES "${ZLIB_INCLUDE_DIRS}") # Public wolfSSL headers require zlib headers @@ -835,38 +968,38 @@ macro(openssl_check_symbol_exists _symbol _files _variable _extra_libs) endif() list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DHAVE_UINTPTR_T") # to pull in stdint.h (as of wolfSSL v5.5.4) endif() - list(APPEND CMAKE_REQUIRED_LIBRARIES "${_extra_libs}") check_symbol_exists("${_symbol}" "${_files}" "${_variable}") cmake_pop_check_state() endmacro() # Ensure that the OpenSSL fork actually supports QUIC. -macro(openssl_check_quic) - if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD) +macro(curl_openssl_check_quic) + if(NOT DEFINED HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT) if(USE_OPENSSL) - openssl_check_symbol_exists("SSL_CTX_set_quic_method" "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD "") - elseif(USE_WOLFSSL) - openssl_check_symbol_exists("wolfSSL_set_quic_method" "wolfssl/options.h;wolfssl/openssl/ssl.h" - HAVE_SSL_CTX_SET_QUIC_METHOD "") + curl_openssl_check_symbol_exists("SSL_set_quic_use_legacy_codepoint" "openssl/ssl.h" HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT) + endif() + if(USE_WOLFSSL) + curl_openssl_check_symbol_exists("wolfSSL_set_quic_use_legacy_codepoint" "wolfssl/options.h;wolfssl/openssl/ssl.h" + HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT) endif() endif() - if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) + if(NOT HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT) message(FATAL_ERROR "QUIC support is missing in OpenSSL fork. Try setting -DOPENSSL_ROOT_DIR") endif() endmacro() if(USE_WOLFSSL) - openssl_check_symbol_exists("wolfSSL_DES_ecb_encrypt" "wolfssl/options.h;wolfssl/openssl/des.h" HAVE_WOLFSSL_DES_ECB_ENCRYPT "") - openssl_check_symbol_exists("wolfSSL_BIO_new" "wolfssl/options.h;wolfssl/ssl.h" HAVE_WOLFSSL_BIO "") - openssl_check_symbol_exists("wolfSSL_BIO_set_shutdown" "wolfssl/options.h;wolfssl/ssl.h" HAVE_WOLFSSL_FULL_BIO "") + curl_openssl_check_symbol_exists("wolfSSL_DES_ecb_encrypt" "wolfssl/options.h;wolfssl/openssl/des.h" HAVE_WOLFSSL_DES_ECB_ENCRYPT) + curl_openssl_check_symbol_exists("wolfSSL_BIO_new" "wolfssl/options.h;wolfssl/ssl.h" HAVE_WOLFSSL_BIO) + curl_openssl_check_symbol_exists("wolfSSL_BIO_set_shutdown" "wolfssl/options.h;wolfssl/ssl.h" HAVE_WOLFSSL_FULL_BIO) endif() if(USE_OPENSSL OR USE_WOLFSSL) if(NOT DEFINED HAVE_SSL_SET0_WBIO) - openssl_check_symbol_exists("SSL_set0_wbio" "openssl/ssl.h" HAVE_SSL_SET0_WBIO "") + curl_openssl_check_symbol_exists("SSL_set0_wbio" "openssl/ssl.h" HAVE_SSL_SET0_WBIO) endif() if(NOT DEFINED HAVE_OPENSSL_SRP AND NOT CURL_DISABLE_SRP) - openssl_check_symbol_exists("SSL_CTX_set_srp_username" "openssl/ssl.h" HAVE_OPENSSL_SRP "") + curl_openssl_check_symbol_exists("SSL_CTX_set_srp_username" "openssl/ssl.h" HAVE_OPENSSL_SRP) endif() endif() @@ -875,32 +1008,53 @@ option(USE_ECH "Enable ECH support" OFF) if(USE_ECH) if(USE_OPENSSL OR USE_WOLFSSL) # Be sure that the TLS library actually supports ECH. - if(NOT DEFINED HAVE_ECH) - if(USE_OPENSSL AND (HAVE_BORINGSSL OR HAVE_AWSLC)) - openssl_check_symbol_exists("SSL_set1_ech_config_list" "openssl/ssl.h" HAVE_ECH "") - elseif(USE_OPENSSL) - openssl_check_symbol_exists("SSL_ech_set1_echconfig" "openssl/ech.h" HAVE_ECH "") - elseif(USE_WOLFSSL) - openssl_check_symbol_exists("wolfSSL_CTX_GenerateEchConfig" "wolfssl/options.h;wolfssl/ssl.h" HAVE_ECH "") - endif() + if(USE_WOLFSSL) + curl_openssl_check_symbol_exists("wolfSSL_CTX_GenerateEchConfig" "wolfssl/options.h;wolfssl/ssl.h" + HAVE_WOLFSSL_CTX_GENERATEECHCONFIG) + endif() + if(HAVE_BORINGSSL OR HAVE_AWSLC) + curl_openssl_check_symbol_exists("SSL_set1_ech_config_list" "openssl/ssl.h" HAVE_SSL_SET1_ECH_CONFIG_LIST) + elseif(HAVE_OPENSSL) + curl_openssl_check_symbol_exists("SSL_set1_ech_config_list" "openssl/ech.h" HAVE_SSL_SET1_ECH_CONFIG_LIST) + endif() + if(HAVE_WOLFSSL_CTX_GENERATEECHCONFIG OR + HAVE_SSL_SET1_ECH_CONFIG_LIST) + set(HAVE_ECH 1) endif() if(NOT HAVE_ECH) message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL") else() - message(STATUS "ECH enabled.") + message(STATUS "ECH enabled") + # ECH wants HTTPSRR + set(USE_HTTPSRR ON) + message(STATUS "HTTPSRR enabled") endif() else() message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL, AWS-LC or wolfSSL") endif() endif() +option(USE_SSLS_EXPORT "Enable SSL session export support" OFF) +if(USE_SSLS_EXPORT) + if(_ssl_enabled) + message(STATUS "SSL export enabled.") + else() + message(FATAL_ERROR "SSL session export requires SSL enabled") + endif() +endif() + option(USE_NGHTTP2 "Use nghttp2 library" ON) if(USE_NGHTTP2) find_package(NGHTTP2) if(NGHTTP2_FOUND) - include_directories(SYSTEM ${NGHTTP2_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${NGHTTP2_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libnghttp2") + list(APPEND CURL_LIBDIRS ${NGHTTP2_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGHTTP2_PC_REQUIRES}) + include_directories(SYSTEM ${NGHTTP2_INCLUDE_DIRS}) + link_directories(${NGHTTP2_LIBRARY_DIRS}) + if(NGHTTP2_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NGHTTP2_CFLAGS}") + endif() else() set(USE_NGHTTP2 OFF) endif() @@ -911,30 +1065,39 @@ if(USE_NGTCP2) if(USE_OPENSSL OR USE_WOLFSSL) if(USE_WOLFSSL) find_package(NGTCP2 REQUIRED "wolfSSL") - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2_crypto_wolfssl") elseif(HAVE_BORINGSSL OR HAVE_AWSLC) find_package(NGTCP2 REQUIRED "BoringSSL") - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2_crypto_boringssl") else() find_package(NGTCP2 REQUIRED "quictls") - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2_crypto_quictls") + if(NOT HAVE_LIBRESSL) + set(_openssl "quictls") + endif() endif() - openssl_check_quic() + curl_openssl_check_quic() elseif(USE_GNUTLS) find_package(NGTCP2 REQUIRED "GnuTLS") - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2_crypto_gnutls") else() message(FATAL_ERROR "ngtcp2 requires OpenSSL, wolfSSL or GnuTLS") endif() - include_directories(SYSTEM ${NGTCP2_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${NGTCP2_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libngtcp2") + list(APPEND CURL_LIBDIRS ${NGTCP2_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGTCP2_PC_REQUIRES}) + include_directories(SYSTEM ${NGTCP2_INCLUDE_DIRS}) + link_directories(${NGTCP2_LIBRARY_DIRS}) + if(NGTCP2_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NGTCP2_CFLAGS}") + endif() find_package(NGHTTP3 REQUIRED) set(USE_NGHTTP3 ON) - include_directories(SYSTEM ${NGHTTP3_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libnghttp3") + list(APPEND CURL_LIBDIRS ${NGHTTP3_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGHTTP3_PC_REQUIRES}) + include_directories(SYSTEM ${NGHTTP3_INCLUDE_DIRS}) + link_directories(${NGHTTP3_LIBRARY_DIRS}) + if(NGHTTP3_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NGHTTP3_CFLAGS}") + endif() endif() option(USE_QUICHE "Use quiche library for HTTP/3 support" OFF) @@ -946,10 +1109,10 @@ if(USE_QUICHE) if(NOT HAVE_BORINGSSL) message(FATAL_ERROR "quiche requires BoringSSL") endif() - openssl_check_quic() + curl_openssl_check_quic() list(APPEND CURL_LIBS ${QUICHE_LIBRARIES}) list(APPEND CURL_LIBDIRS ${QUICHE_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "quiche") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${QUICHE_PC_REQUIRES}) include_directories(SYSTEM ${QUICHE_INCLUDE_DIRS}) link_directories(${QUICHE_LIBRARY_DIRS}) if(QUICHE_CFLAGS) @@ -957,8 +1120,8 @@ if(USE_QUICHE) endif() if(NOT DEFINED HAVE_QUICHE_CONN_SET_QLOG_FD) cmake_push_check_state() - set(CMAKE_REQUIRED_INCLUDES "${QUICHE_INCLUDE_DIRS}") - set(CMAKE_REQUIRED_LIBRARIES "${QUICHE_LIBRARIES}") + list(APPEND CMAKE_REQUIRED_INCLUDES "${QUICHE_INCLUDE_DIRS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES "${QUICHE_LIBRARIES}") check_symbol_exists("quiche_conn_set_qlog_fd" "quiche.h" HAVE_QUICHE_CONN_SET_QLOG_FD) cmake_pop_check_state() endif() @@ -973,7 +1136,7 @@ if(USE_MSH3) if(NOT USE_OPENSSL) message(FATAL_ERROR "msh3/msquic requires OpenSSL fork with QUIC API") endif() - openssl_check_quic() + curl_openssl_check_quic() endif() find_package(MSH3 REQUIRED) list(APPEND CURL_LIBS ${MSH3_LIBRARIES}) @@ -996,7 +1159,7 @@ if(USE_OPENSSL_QUIC) set(USE_NGHTTP3 ON) include_directories(SYSTEM ${NGHTTP3_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libnghttp3") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGHTTP3_PC_REQUIRES}) endif() if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)) @@ -1020,59 +1183,30 @@ if(NOT CURL_DISABLE_LDAP) # Now that we know, we are not using Windows LDAP... if(NOT USE_WIN32_LDAP) - if(NOT DEFINED LDAP_LIBRARY) - set(LDAP_LIBRARY "ldap" CACHE STRING "Name or full path to ldap library") - endif() - if(NOT DEFINED LDAP_LBER_LIBRARY) - set(LDAP_LBER_LIBRARY "lber" CACHE STRING "Name or full path to lber library") - endif() - if(NOT DEFINED LDAP_INCLUDE_DIR) - set(LDAP_INCLUDE_DIR "" CACHE STRING "Path to LDAP include directory") - endif() - # Check for LDAP cmake_push_check_state() if(USE_OPENSSL) - set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) - endif() - check_library_exists("${LDAP_LIBRARY}" "ldap_init" "" HAVE_LIBLDAP) - if(HAVE_LIBLDAP) - check_library_exists("${LDAP_LIBRARY};${LDAP_LBER_LIBRARY}" "ber_init" "" HAVE_LIBLBER) - else() - check_library_exists("${LDAP_LBER_LIBRARY}" "ber_init" "" HAVE_LIBLBER) - endif() - - if(LDAP_INCLUDE_DIR) - list(APPEND CMAKE_REQUIRED_INCLUDES ${LDAP_INCLUDE_DIR}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) endif() + find_package(LDAP) + if(LDAP_FOUND) + set(HAVE_LBER_H 1) + set(CURL_LIBS "${LDAP_LIBRARIES};${CURL_LIBS}") + list(APPEND CURL_LIBDIRS ${LDAP_LIBRARY_DIRS}) + if(LDAP_PC_REQUIRES) + set(LIBCURL_PC_REQUIRES_PRIVATE "${LDAP_PC_REQUIRES};${LIBCURL_PC_REQUIRES_PRIVATE}") + endif() + include_directories(SYSTEM ${LDAP_INCLUDE_DIRS}) + link_directories(${LDAP_LIBRARY_DIRS}) + if(LDAP_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LDAP_CFLAGS}") + endif() - unset(_include_list) - check_include_file("lber.h" HAVE_LBER_H) - if(HAVE_LBER_H) - list(APPEND _include_list "lber.h") - endif() - check_include_files("${_include_list};ldap.h" HAVE_LDAP_H) - unset(_include_list) + # LDAP feature checks - if(NOT HAVE_LDAP_H) - message(STATUS "LDAP_H not found CURL_DISABLE_LDAP set ON") - set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE) - elseif(NOT HAVE_LIBLDAP) - message(STATUS "LDAP library '${LDAP_LIBRARY}' not found CURL_DISABLE_LDAP set ON") - set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE) - else() - if(LDAP_INCLUDE_DIR) - include_directories(SYSTEM ${LDAP_INCLUDE_DIR}) - endif() list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DLDAP_DEPRECATED=1") - list(APPEND CMAKE_REQUIRED_LIBRARIES ${LDAP_LIBRARY}) - set(CURL_LIBS "${LDAP_LIBRARY};${CURL_LIBS}") - # FIXME: uncomment once pkg-config-based detection landed: https://github.com/curl/curl/pull/15273 - # set(LIBCURL_PC_REQUIRES_PRIVATE "${LDAP_PC_REQUIRES};${LIBCURL_PC_REQUIRES_PRIVATE}") - if(HAVE_LIBLBER) - list(APPEND CMAKE_REQUIRED_LIBRARIES ${LDAP_LBER_LIBRARY}) - set(CURL_LIBS "${LDAP_LBER_LIBRARY};${CURL_LIBS}") - endif() + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LDAP_LIBRARIES}) + curl_required_libpaths("${LDAP_LIBRARY_DIRS}") check_function_exists("ldap_url_parse" HAVE_LDAP_URL_PARSE) check_function_exists("ldap_init_fd" HAVE_LDAP_INIT_FD) @@ -1086,6 +1220,9 @@ if(NOT CURL_DISABLE_LDAP) if(NOT CURL_DISABLE_LDAPS) set(HAVE_LDAP_SSL ON) endif() + else() + message(STATUS "LDAP not found. CURL_DISABLE_LDAP set ON") + set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE) endif() cmake_pop_check_state() endif() @@ -1112,7 +1249,7 @@ if(APPLE) option(USE_APPLE_IDN "Use Apple built-in IDN support" OFF) if(USE_APPLE_IDN) cmake_push_check_state() - set(CMAKE_REQUIRED_LIBRARIES "icucore") + list(APPEND CMAKE_REQUIRED_LIBRARIES "icucore") check_symbol_exists("uidna_openUTS46" "unicode/uidna.h" HAVE_APPLE_IDN) cmake_pop_check_state() if(HAVE_APPLE_IDN) @@ -1134,7 +1271,7 @@ if(USE_LIBIDN2 AND NOT USE_APPLE_IDN AND NOT USE_WIN32_IDN) if(LIBIDN2_FOUND) set(CURL_LIBS "${LIBIDN2_LIBRARIES};${CURL_LIBS}") list(APPEND CURL_LIBDIRS ${LIBIDN2_LIBRARY_DIRS}) - set(LIBCURL_PC_REQUIRES_PRIVATE "libidn2;${LIBCURL_PC_REQUIRES_PRIVATE}") + set(LIBCURL_PC_REQUIRES_PRIVATE "${LIBIDN2_PC_REQUIRES};${LIBCURL_PC_REQUIRES_PRIVATE}") include_directories(SYSTEM ${LIBIDN2_INCLUDE_DIRS}) link_directories(${LIBIDN2_LIBRARY_DIRS}) if(LIBIDN2_CFLAGS) @@ -1151,15 +1288,16 @@ mark_as_advanced(CURL_USE_LIBPSL) set(USE_LIBPSL OFF) if(CURL_USE_LIBPSL) - find_package(Libpsl) # TODO: add REQUIRED to match autotools - if(LIBPSL_FOUND) - list(APPEND CURL_LIBS ${LIBPSL_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libpsl") - include_directories(SYSTEM ${LIBPSL_INCLUDE_DIRS}) - set(USE_LIBPSL ON) - else() - message(WARNING "libpsl is enabled, but not found.") + find_package(Libpsl REQUIRED) + list(APPEND CURL_LIBS ${LIBPSL_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBPSL_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBPSL_PC_REQUIRES}) + include_directories(SYSTEM ${LIBPSL_INCLUDE_DIRS}) + link_directories(${LIBPSL_LIBRARY_DIRS}) + if(LIBPSL_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBPSL_CFLAGS}") endif() + set(USE_LIBPSL ON) endif() # libssh2 @@ -1171,8 +1309,13 @@ if(CURL_USE_LIBSSH2) find_package(Libssh2) if(LIBSSH2_FOUND) list(APPEND CURL_LIBS ${LIBSSH2_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libssh2") + list(APPEND CURL_LIBDIRS ${LIBSSH2_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBSSH2_PC_REQUIRES}) include_directories(SYSTEM ${LIBSSH2_INCLUDE_DIRS}) + link_directories(${LIBSSH2_LIBRARY_DIRS}) + if(LIBSSH2_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBSSH2_CFLAGS}") + endif() set(USE_LIBSSH2 ON) endif() endif() @@ -1182,17 +1325,15 @@ option(CURL_USE_LIBSSH "Use libssh" OFF) mark_as_advanced(CURL_USE_LIBSSH) if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH) find_package(Libssh REQUIRED) - if(LIBSSH_FOUND) - list(APPEND CURL_LIBS ${LIBSSH_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${LIBSSH_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libssh") - include_directories(SYSTEM ${LIBSSH_INCLUDE_DIRS}) - link_directories(${LIBSSH_LIBRARY_DIRS}) - if(LIBSSH_CFLAGS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBSSH_CFLAGS}") - endif() - set(USE_LIBSSH ON) + list(APPEND CURL_LIBS ${LIBSSH_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBSSH_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBSSH_PC_REQUIRES}) + include_directories(SYSTEM ${LIBSSH_INCLUDE_DIRS}) + link_directories(${LIBSSH_LIBRARY_DIRS}) + if(LIBSSH_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBSSH_CFLAGS}") endif() + set(USE_LIBSSH ON) endif() # wolfSSH @@ -1216,17 +1357,15 @@ option(CURL_USE_GSASL "Use libgsasl" OFF) mark_as_advanced(CURL_USE_GSASL) if(CURL_USE_GSASL) find_package(Libgsasl REQUIRED) - if(LIBGSASL_FOUND) - list(APPEND CURL_LIBS ${LIBGSASL_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${LIBGSASL_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libgsasl") - include_directories(SYSTEM ${LIBGSASL_INCLUDE_DIRS}) - link_directories(${LIBGSASL_LIBRARY_DIRS}) - if(LIBGSASL_CFLAGS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBGSASL_CFLAGS}") - endif() - set(USE_GSASL ON) + list(APPEND CURL_LIBS ${LIBGSASL_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBGSASL_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBGSASL_PC_REQUIRES}) + include_directories(SYSTEM ${LIBGSASL_INCLUDE_DIRS}) + link_directories(${LIBGSASL_LIBRARY_DIRS}) + if(LIBGSASL_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBGSASL_CFLAGS}") endif() + set(USE_GSASL ON) endif() option(CURL_USE_GSSAPI "Use GSSAPI implementation" OFF) @@ -1237,6 +1376,15 @@ if(CURL_USE_GSSAPI) set(HAVE_GSSAPI ${GSS_FOUND}) if(GSS_FOUND) + list(APPEND CURL_LIBS ${GSS_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${GSS_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${GSS_PC_REQUIRES}) + include_directories(SYSTEM ${GSS_INCLUDE_DIRS}) + link_directories(${GSS_LIBRARY_DIRS}) + if(GSS_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_CFLAGS}") + endif() + if(GSS_FLAVOUR STREQUAL "GNU") set(HAVE_GSSGNU 1) else() @@ -1260,8 +1408,8 @@ if(CURL_USE_GSSAPI) endif() if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE) - set(CMAKE_REQUIRED_FLAGS ${GSS_CFLAGS}) - set(CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES}) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${GSS_CFLAGS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES}) curl_required_libpaths("${GSS_LIBRARY_DIRS}") check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" "${_include_list}" HAVE_GSS_C_NT_HOSTBASED_SERVICE) endif() @@ -1272,15 +1420,6 @@ if(CURL_USE_GSSAPI) unset(_include_list) cmake_pop_check_state() endif() - - list(APPEND CURL_LIBS ${GSS_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${GSS_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${GSS_PC_REQUIRES}) - include_directories(SYSTEM ${GSS_INCLUDE_DIRS}) - link_directories(${GSS_LIBRARY_DIRS}) - if(GSS_CFLAGS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_CFLAGS}") - endif() else() message(WARNING "GSSAPI has been requested, but no supporting libraries found. Skipping.") endif() @@ -1293,42 +1432,34 @@ if(CURL_USE_LIBUV) message(FATAL_ERROR "Using libuv without debug support enabled is useless") endif() find_package(Libuv REQUIRED) - if(LIBUV_FOUND) - list(APPEND CURL_LIBS ${LIBUV_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${LIBUV_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libuv") - include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) - link_directories(${LIBUV_LIBRARY_DIRS}) - if(LIBUV_CFLAGS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBUV_CFLAGS}") - endif() - set(USE_LIBUV ON) - set(HAVE_UV_H ON) + list(APPEND CURL_LIBS ${LIBUV_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBUV_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBUV_PC_REQUIRES}) + include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) + link_directories(${LIBUV_LIBRARY_DIRS}) + if(LIBUV_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBUV_CFLAGS}") endif() + set(USE_LIBUV ON) + set(HAVE_UV_H ON) endif() option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF) if(USE_LIBRTMP) - set(_extra_libs "rtmp") - if(WIN32) - list(APPEND _extra_libs "winmm") - endif() - openssl_check_symbol_exists("RTMP_Init" "librtmp/rtmp.h" HAVE_LIBRTMP "${_extra_libs}") - if(HAVE_LIBRTMP) - list(APPEND CURL_LIBS "rtmp") - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "librtmp") - if(WIN32) - list(APPEND CURL_LIBS "winmm") - endif() - else() - message(WARNING "librtmp has been requested, but not found or missing OpenSSL. Skipping.") - set(USE_LIBRTMP OFF) + find_package(Librtmp REQUIRED) + list(APPEND CURL_LIBS ${LIBRTMP_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBRTMP_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBRTMP_PC_REQUIRES}) + include_directories(SYSTEM ${LIBRTMP_INCLUDE_DIRS}) + link_directories(${LIBRTMP_LIBRARY_DIRS}) + if(LIBRTMP_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBRTMP_CFLAGS}") endif() endif() option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON) if(ENABLE_UNIX_SOCKETS) - if(WIN32) + if(WIN32 OR DOS) set(USE_UNIX_SOCKETS ON) else() include(CheckStructHasMember) @@ -1344,8 +1475,8 @@ endif() if(_curl_ca_bundle_supported) set(CURL_CA_BUNDLE "auto" CACHE STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") - set(CURL_CA_FALLBACK OFF CACHE BOOL - "Use built-in CA store of TLS backend. Defaults to OFF") + set(CURL_CA_FALLBACK OFF CACHE + BOOL "Use built-in CA store of TLS backend. Defaults to OFF") set(CURL_CA_PATH "auto" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") set(CURL_CA_EMBED "" CACHE @@ -1460,40 +1591,38 @@ endif() # Detect headers -# Use check_include_file_concat() for headers required by subsequent -# check_include_file_concat() or check_symbol_exists() detections. +# Use check_include_file_concat_curl() for headers required by subsequent +# check_include_file_concat_curl() or check_symbol_exists() detections. # Order for these is significant. check_include_file("sys/eventfd.h" HAVE_SYS_EVENTFD_H) check_include_file("sys/filio.h" HAVE_SYS_FILIO_H) -check_include_file("sys/wait.h" HAVE_SYS_WAIT_H) check_include_file("sys/ioctl.h" HAVE_SYS_IOCTL_H) check_include_file("sys/param.h" HAVE_SYS_PARAM_H) check_include_file("sys/poll.h" HAVE_SYS_POLL_H) check_include_file("sys/resource.h" HAVE_SYS_RESOURCE_H) -check_include_file_concat("sys/select.h" HAVE_SYS_SELECT_H) -check_include_file_concat("sys/socket.h" HAVE_SYS_SOCKET_H) +check_include_file_concat_curl("sys/select.h" HAVE_SYS_SELECT_H) +check_include_file_concat_curl("sys/socket.h" HAVE_SYS_SOCKET_H) check_include_file("sys/sockio.h" HAVE_SYS_SOCKIO_H) check_include_file("sys/stat.h" HAVE_SYS_STAT_H) -check_include_file_concat("sys/time.h" HAVE_SYS_TIME_H) -check_include_file_concat("sys/types.h" HAVE_SYS_TYPES_H) +check_include_file_concat_curl("sys/time.h" HAVE_SYS_TIME_H) +check_include_file_concat_curl("sys/types.h" HAVE_SYS_TYPES_H) check_include_file("sys/un.h" HAVE_SYS_UN_H) -check_include_file("sys/utime.h" HAVE_SYS_UTIME_H) -check_include_file("sys/xattr.h" HAVE_SYS_XATTR_H) +check_include_file_concat_curl("sys/utime.h" HAVE_SYS_UTIME_H) # sys/types.h (AmigaOS) -check_include_file_concat("arpa/inet.h" HAVE_ARPA_INET_H) +check_include_file_concat_curl("arpa/inet.h" HAVE_ARPA_INET_H) check_include_file("dirent.h" HAVE_DIRENT_H) check_include_file("fcntl.h" HAVE_FCNTL_H) -check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H) +check_include_file_concat_curl("ifaddrs.h" HAVE_IFADDRS_H) check_include_file("io.h" HAVE_IO_H) -check_include_file_concat("libgen.h" HAVE_LIBGEN_H) +check_include_file_concat_curl("libgen.h" HAVE_LIBGEN_H) check_include_file("linux/tcp.h" HAVE_LINUX_TCP_H) check_include_file("locale.h" HAVE_LOCALE_H) -check_include_file("net/if.h" HAVE_NET_IF_H) -check_include_file_concat("netdb.h" HAVE_NETDB_H) -check_include_file_concat("netinet/in.h" HAVE_NETINET_IN_H) +check_include_file_concat_curl("net/if.h" HAVE_NET_IF_H) # sys/select.h (e.g. MS-DOS/Watt-32) +check_include_file_concat_curl("netdb.h" HAVE_NETDB_H) +check_include_file_concat_curl("netinet/in.h" HAVE_NETINET_IN_H) check_include_file("netinet/in6.h" HAVE_NETINET_IN6_H) -check_include_file_concat("netinet/tcp.h" HAVE_NETINET_TCP_H) # sys/types.h (e.g. Cygwin) netinet/in.h -check_include_file_concat("netinet/udp.h" HAVE_NETINET_UDP_H) # sys/types.h (e.g. Cygwin) +check_include_file_concat_curl("netinet/tcp.h" HAVE_NETINET_TCP_H) # sys/types.h (e.g. Cygwin) netinet/in.h +check_include_file_concat_curl("netinet/udp.h" HAVE_NETINET_UDP_H) # sys/types.h (e.g. Cygwin) check_include_file("poll.h" HAVE_POLL_H) check_include_file("pwd.h" HAVE_PWD_H) check_include_file("stdatomic.h" HAVE_STDATOMIC_H) @@ -1502,11 +1631,11 @@ check_include_file("strings.h" HAVE_STRINGS_H) check_include_file("stropts.h" HAVE_STROPTS_H) check_include_file("termio.h" HAVE_TERMIO_H) check_include_file("termios.h" HAVE_TERMIOS_H) -check_include_file_concat("unistd.h" HAVE_UNISTD_H) +check_include_file_concat_curl("unistd.h" HAVE_UNISTD_H) check_include_file("utime.h" HAVE_UTIME_H) -if(CMAKE_SYSTEM_NAME MATCHES "AmigaOS") - check_include_file_concat("proto/bsdsocket.h" HAVE_PROTO_BSDSOCKET_H) +if(AMIGA) + check_include_file_concat_curl("proto/bsdsocket.h" HAVE_PROTO_BSDSOCKET_H) endif() # Pass these detection results to curl_internal_test() for use in CurlTests.c @@ -1551,10 +1680,14 @@ if(SIZEOF_SUSECONDS_T) endif() # Check for some functions that are used + +# Apply to all feature checks if(WIN32) - list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") # Apply to all feature checks + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") elseif(HAVE_LIBSOCKET) - list(APPEND CMAKE_REQUIRED_LIBRARIES "socket") # Apply to all feature checks + list(APPEND CMAKE_REQUIRED_LIBRARIES "socket") +elseif(DOS) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${WATT_ROOT}/lib/libwatt.a") endif() check_function_exists("fnmatch" HAVE_FNMATCH) @@ -1562,7 +1695,6 @@ check_symbol_exists("basename" "${CURL_INCLUDES};string.h" HAVE_BASENAME) check_symbol_exists("opendir" "dirent.h" HAVE_OPENDIR) check_function_exists("poll" HAVE_POLL) # poll.h check_symbol_exists("socket" "${CURL_INCLUDES}" HAVE_SOCKET) # winsock2.h sys/socket.h -check_function_exists("sched_yield" HAVE_SCHED_YIELD) check_symbol_exists("socketpair" "${CURL_INCLUDES}" HAVE_SOCKETPAIR) # sys/socket.h check_symbol_exists("recv" "${CURL_INCLUDES}" HAVE_RECV) # proto/bsdsocket.h sys/types.h sys/socket.h check_symbol_exists("send" "${CURL_INCLUDES}" HAVE_SEND) # proto/bsdsocket.h sys/types.h sys/socket.h @@ -1571,9 +1703,6 @@ check_function_exists("sendmmsg" HAVE_SENDMMSG) check_symbol_exists("select" "${CURL_INCLUDES}" HAVE_SELECT) # proto/bsdsocket.h sys/select.h sys/socket.h check_symbol_exists("strdup" "string.h" HAVE_STRDUP) check_symbol_exists("strtok_r" "string.h" HAVE_STRTOK_R) -check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) -check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) -check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) check_symbol_exists("memrchr" "string.h" HAVE_MEMRCHR) check_symbol_exists("alarm" "unistd.h" HAVE_ALARM) check_symbol_exists("fcntl" "fcntl.h" HAVE_FCNTL) @@ -1591,6 +1720,7 @@ check_function_exists("utime" HAVE_UTIME) check_symbol_exists("gmtime_r" "stdlib.h;time.h" HAVE_GMTIME_R) check_symbol_exists("gethostbyname_r" "netdb.h" HAVE_GETHOSTBYNAME_R) +check_symbol_exists("gethostname" "${CURL_INCLUDES}" HAVE_GETHOSTNAME) # winsock2.h unistd.h proto/bsdsocket.h check_symbol_exists("signal" "signal.h" HAVE_SIGNAL) check_symbol_exists("strtoll" "stdlib.h" HAVE_STRTOLL) @@ -1611,11 +1741,18 @@ check_function_exists("setlocale" HAVE_SETLOCALE) check_function_exists("setmode" HAVE_SETMODE) check_function_exists("setrlimit" HAVE_SETRLIMIT) +if(NOT WIN32) + check_function_exists("sched_yield" HAVE_SCHED_YIELD) + check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) + check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) + check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) +endif() + if(WIN32 OR CYGWIN) check_function_exists("_setmode" HAVE__SETMODE) endif() -if(CMAKE_SYSTEM_NAME MATCHES "AmigaOS") +if(AMIGA) check_symbol_exists("CloseSocket" "${CURL_INCLUDES}" HAVE_CLOSESOCKET_CAMEL) # sys/socket.h proto/bsdsocket.h endif() @@ -1631,9 +1768,6 @@ if(APPLE) check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) endif() check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h -if(MSVC AND (MSVC_VERSION LESS_EQUAL 1600)) - set(HAVE_INET_NTOP OFF) -endif() check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h check_symbol_exists("fsetxattr" "sys/xattr.h" HAVE_FSETXATTR) @@ -1644,11 +1778,11 @@ endif() cmake_push_check_state() if(WIN32) - set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") + list(APPEND CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY) set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY}) elseif(HAVE_SYS_SOCKET_H) - set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") + list(APPEND CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T) set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T}) endif() @@ -1664,7 +1798,6 @@ foreach(_curl_test IN ITEMS HAVE_IOCTL_FIONBIO HAVE_IOCTL_SIOCGIFADDR HAVE_SETSOCKOPT_SO_NONBLOCK - HAVE_O_NONBLOCK HAVE_GETHOSTBYNAME_R_3 HAVE_GETHOSTBYNAME_R_5 HAVE_GETHOSTBYNAME_R_6 @@ -1683,25 +1816,27 @@ endforeach() cmake_push_check_state() if(HAVE_FILE_OFFSET_BITS) set(_FILE_OFFSET_BITS 64) - set(CMAKE_REQUIRED_DEFINITIONS "-D_FILE_OFFSET_BITS=64") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_FILE_OFFSET_BITS=64") endif() check_type_size("off_t" SIZEOF_OFF_T) -# fseeko may not exist with _FILE_OFFSET_BITS=64 but can exist with -# _FILE_OFFSET_BITS unset or 32 (e.g. Android ARMv7 with NDK 26b and API level < 24) -# so we need to test fseeko after testing for _FILE_OFFSET_BITS -check_symbol_exists("fseeko" "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO) +if(NOT WIN32) + # fseeko may not exist with _FILE_OFFSET_BITS=64 but can exist with + # _FILE_OFFSET_BITS unset or 32 (e.g. Android ARMv7 with NDK 26b and API level < 24) + # so we need to test fseeko after testing for _FILE_OFFSET_BITS + check_symbol_exists("fseeko" "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO) -if(HAVE_FSEEKO) - set(HAVE_DECL_FSEEKO 1) + if(HAVE_FSEEKO) + set(HAVE_DECL_FSEEKO 1) + endif() endif() # Include this header to get the type cmake_push_check_state() -set(CMAKE_REQUIRED_INCLUDES "${PROJECT_SOURCE_DIR}/include") -set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h") +list(APPEND CMAKE_REQUIRED_INCLUDES "${PROJECT_SOURCE_DIR}/include") +list(APPEND CMAKE_EXTRA_INCLUDE_FILES "curl/system.h") check_type_size("curl_off_t" SIZEOF_CURL_OFF_T) -set(CMAKE_EXTRA_INCLUDE_FILES "curl/curl.h") +list(APPEND CMAKE_EXTRA_INCLUDE_FILES "curl/curl.h") check_type_size("curl_socket_t" SIZEOF_CURL_SOCKET_T) cmake_pop_check_state() # pop curl system headers cmake_pop_check_state() # pop -D_FILE_OFFSET_BITS=64 @@ -1718,6 +1853,16 @@ if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) }" HAVE_WRITABLE_ARGV) endif() +if(NOT CMAKE_CROSSCOMPILING) + include(CheckCSourceRuns) + check_c_source_runs(" + #include + int main(void) { + time_t t = -1; + return t < 0; + }" HAVE_TIME_T_UNSIGNED) +endif() + curl_internal_test(HAVE_GLIBC_STRERROR_R) curl_internal_test(HAVE_POSIX_STRERROR_R) @@ -1759,15 +1904,6 @@ if(NOT HAVE_IN_ADDR_T) set(in_addr_t "unsigned long") endif() -# Check for nonblocking -set(HAVE_DISABLED_NONBLOCKING 1) -if(HAVE_FIONBIO OR - HAVE_IOCTLSOCKET OR - HAVE_IOCTLSOCKET_CASE OR - HAVE_O_NONBLOCK) - unset(HAVE_DISABLED_NONBLOCKING) -endif() - if(CMAKE_COMPILER_IS_GNUCC AND APPLE) include(CheckCCompilerFlag) check_c_compiler_flag("-Wno-long-double" HAVE_C_FLAG_Wno_long_double) @@ -1793,9 +1929,11 @@ include(CMake/OtherTests.cmake) add_definitions("-DHAVE_CONFIG_H") -# For Windows, all compilers used by CMake should support large files if(WIN32) - set(USE_WIN32_LARGE_FILES ON) + # _fseeki64() requires VS2005 + if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1400)) + set(USE_WIN32_LARGE_FILES ON) + endif() # Use the manifest embedded in the Windows Resource set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -DCURL_EMBED_MANIFEST") @@ -1813,24 +1951,23 @@ endif() if(MSVC) # Disable default manifest added by CMake - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -MANIFEST:NO") - add_definitions("-D_CRT_SECURE_NO_DEPRECATE" "-D_CRT_NONSTDC_NO_DEPRECATE") - if(CMAKE_C_FLAGS MATCHES "/W[0-4]") - string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + if(CMAKE_C_FLAGS MATCHES "[/-]W[0-4]") + string(REGEX REPLACE "[/-]W[0-4]" "-W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") else() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -W4") endif() - # Use multithreaded compilation on VS 2008+ + # Use multithreaded compilation on VS2008+ if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER_EQUAL 1500) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MP") endif() endif() if(CURL_WERROR) if(MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -WX") else() # This assumes clang or gcc style options set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") @@ -1856,7 +1993,7 @@ endif() # Ugly (but functional) way to include "Makefile.inc" by transforming it # (= regenerate it). -function(transform_makefile_inc _input_file _output_file) +function(curl_transform_makefile_inc _input_file _output_file) file(READ ${_input_file} _makefile_inc_text) string(REPLACE "$(top_srcdir)" "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text}) string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text}) @@ -1912,7 +2049,7 @@ endif() # Helper to populate a list (_items) with a label when conditions # (the remaining args) are satisfied -macro(_add_if _label) +macro(curl_add_if _label) # Needs to be a macro to allow this indirection if(${ARGN}) set(_items ${_items} "${_label}") @@ -1932,90 +2069,94 @@ if(NOT CURL_DISABLE_NTLM AND endif() # Clear list and try to detect available protocols -unset(_items) -_add_if("HTTP" NOT CURL_DISABLE_HTTP) -_add_if("HTTPS" NOT CURL_DISABLE_HTTP AND _ssl_enabled) -_add_if("FTP" NOT CURL_DISABLE_FTP) -_add_if("FTPS" NOT CURL_DISABLE_FTP AND _ssl_enabled) -_add_if("FILE" NOT CURL_DISABLE_FILE) -_add_if("TELNET" NOT CURL_DISABLE_TELNET) -_add_if("LDAP" NOT CURL_DISABLE_LDAP) +set(_items "") +curl_add_if("HTTP" NOT CURL_DISABLE_HTTP) +curl_add_if("HTTPS" NOT CURL_DISABLE_HTTP AND _ssl_enabled) +curl_add_if("FTP" NOT CURL_DISABLE_FTP) +curl_add_if("FTPS" NOT CURL_DISABLE_FTP AND _ssl_enabled) +curl_add_if("FILE" NOT CURL_DISABLE_FILE) +curl_add_if("TELNET" NOT CURL_DISABLE_TELNET) +curl_add_if("LDAP" NOT CURL_DISABLE_LDAP) # CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS -_add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND - ((USE_OPENLDAP AND _ssl_enabled) OR - (NOT USE_OPENLDAP AND HAVE_LDAP_SSL))) -_add_if("DICT" NOT CURL_DISABLE_DICT) -_add_if("TFTP" NOT CURL_DISABLE_TFTP) -_add_if("GOPHER" NOT CURL_DISABLE_GOPHER) -_add_if("GOPHERS" NOT CURL_DISABLE_GOPHER AND _ssl_enabled) -_add_if("POP3" NOT CURL_DISABLE_POP3) -_add_if("POP3S" NOT CURL_DISABLE_POP3 AND _ssl_enabled) -_add_if("IMAP" NOT CURL_DISABLE_IMAP) -_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND _ssl_enabled) -_add_if("SMB" NOT CURL_DISABLE_SMB AND - _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) -_add_if("SMBS" NOT CURL_DISABLE_SMB AND _ssl_enabled AND - _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) -_add_if("SMTP" NOT CURL_DISABLE_SMTP) -_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND _ssl_enabled) -_add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) -_add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) -_add_if("IPFS" NOT CURL_DISABLE_IPFS) -_add_if("IPNS" NOT CURL_DISABLE_IPFS) -_add_if("RTSP" NOT CURL_DISABLE_RTSP) -_add_if("RTMP" USE_LIBRTMP) -_add_if("MQTT" NOT CURL_DISABLE_MQTT) -_add_if("WS" NOT CURL_DISABLE_WEBSOCKETS) -_add_if("WSS" NOT CURL_DISABLE_WEBSOCKETS AND _ssl_enabled) +curl_add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND + ((USE_OPENLDAP AND _ssl_enabled) OR + (NOT USE_OPENLDAP AND HAVE_LDAP_SSL))) +curl_add_if("DICT" NOT CURL_DISABLE_DICT) +curl_add_if("TFTP" NOT CURL_DISABLE_TFTP) +curl_add_if("GOPHER" NOT CURL_DISABLE_GOPHER) +curl_add_if("GOPHERS" NOT CURL_DISABLE_GOPHER AND _ssl_enabled) +curl_add_if("POP3" NOT CURL_DISABLE_POP3) +curl_add_if("POP3S" NOT CURL_DISABLE_POP3 AND _ssl_enabled) +curl_add_if("IMAP" NOT CURL_DISABLE_IMAP) +curl_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND _ssl_enabled) +curl_add_if("SMB" NOT CURL_DISABLE_SMB AND + _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) +curl_add_if("SMBS" NOT CURL_DISABLE_SMB AND _ssl_enabled AND + _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) +curl_add_if("SMTP" NOT CURL_DISABLE_SMTP) +curl_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND _ssl_enabled) +curl_add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) +curl_add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) +curl_add_if("IPFS" NOT CURL_DISABLE_IPFS) +curl_add_if("IPNS" NOT CURL_DISABLE_IPFS) +curl_add_if("RTSP" NOT CURL_DISABLE_RTSP) +curl_add_if("RTMP" USE_LIBRTMP) +curl_add_if("MQTT" NOT CURL_DISABLE_MQTT) +curl_add_if("WS" NOT CURL_DISABLE_WEBSOCKETS) +curl_add_if("WSS" NOT CURL_DISABLE_WEBSOCKETS AND _ssl_enabled) if(_items) list(SORT _items) endif() +set(CURL_SUPPORTED_PROTOCOLS_LIST "${_items}") string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}") string(TOLOWER "${SUPPORT_PROTOCOLS}" _support_protocols_lower) message(STATUS "Protocols: ${_support_protocols_lower}") # Clear list and try to detect available features -unset(_items) -_add_if("SSL" _ssl_enabled) -_add_if("IPv6" ENABLE_IPV6) -_add_if("UnixSockets" USE_UNIX_SOCKETS) -_add_if("libz" HAVE_LIBZ) -_add_if("brotli" HAVE_BROTLI) -_add_if("gsasl" USE_GSASL) -_add_if("zstd" HAVE_ZSTD) -_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) -_add_if("IDN" (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR - USE_WIN32_IDN OR - USE_APPLE_IDN) -_add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND - ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) -_add_if("SSPI" USE_WINDOWS_SSPI) -_add_if("GSS-API" HAVE_GSSAPI) -_add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) -_add_if("HSTS" NOT CURL_DISABLE_HSTS) -_add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND - (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) -_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND - (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) -_add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND - (_use_curl_ntlm_core OR USE_WINDOWS_SSPI)) -_add_if("TLS-SRP" USE_TLS_SRP) -_add_if("HTTP2" USE_NGHTTP2) -_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC) -_add_if("MultiSSL" CURL_WITH_MULTI_SSL) -_add_if("HTTPS-proxy" _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS - OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR - USE_MBEDTLS OR USE_SECTRANSP OR - (USE_WOLFSSL AND HAVE_WOLFSSL_BIO))) -_add_if("Unicode" ENABLE_UNICODE) -_add_if("threadsafe" HAVE_ATOMIC OR - (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR - (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x0600)) -_add_if("Debug" ENABLE_DEBUG) -_add_if("TrackMemory" ENABLE_CURLDEBUG) -_add_if("ECH" _ssl_enabled AND HAVE_ECH) -_add_if("PSL" USE_LIBPSL) -_add_if("CAcert" CURL_CA_EMBED_SET) +set(_items "") +curl_add_if("SSL" _ssl_enabled) +curl_add_if("IPv6" USE_IPV6) +curl_add_if("UnixSockets" USE_UNIX_SOCKETS) +curl_add_if("libz" HAVE_LIBZ) +curl_add_if("brotli" HAVE_BROTLI) +curl_add_if("gsasl" USE_GSASL) +curl_add_if("zstd" HAVE_ZSTD) +curl_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) +curl_add_if("asyn-rr" USE_ARES AND ENABLE_THREADED_RESOLVER) +curl_add_if("IDN" (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR + USE_WIN32_IDN OR + USE_APPLE_IDN) +curl_add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND + ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) +curl_add_if("SSPI" USE_WINDOWS_SSPI) +curl_add_if("GSS-API" HAVE_GSSAPI) +curl_add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) +curl_add_if("HSTS" NOT CURL_DISABLE_HSTS) +curl_add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) +curl_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) +curl_add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND + (_use_curl_ntlm_core OR USE_WINDOWS_SSPI)) +curl_add_if("TLS-SRP" USE_TLS_SRP) +curl_add_if("HTTP2" USE_NGHTTP2) +curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC) +curl_add_if("MultiSSL" CURL_WITH_MULTI_SSL) +curl_add_if("HTTPS-proxy" _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS + OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR + USE_MBEDTLS OR USE_SECTRANSP OR + (USE_WOLFSSL AND HAVE_WOLFSSL_BIO))) +curl_add_if("Unicode" ENABLE_UNICODE) +curl_add_if("threadsafe" HAVE_ATOMIC OR + (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR + (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x0600)) +curl_add_if("Debug" ENABLE_DEBUG) +curl_add_if("TrackMemory" ENABLE_CURLDEBUG) +curl_add_if("ECH" _ssl_enabled AND HAVE_ECH) +curl_add_if("HTTPSRR" _ssl_enabled AND USE_HTTPSRR) +curl_add_if("PSL" USE_LIBPSL) +curl_add_if("CAcert" CURL_CA_EMBED_SET) +curl_add_if("SSLS-EXPORT" _ssl_enabled AND USE_SSLS_EXPORT) if(_items) if(NOT CMAKE_VERSION VERSION_LESS 3.13) list(SORT _items CASE INSENSITIVE) @@ -2023,20 +2164,21 @@ if(_items) list(SORT _items) endif() endif() +set(CURL_SUPPORTED_FEATURES_LIST "${_items}") string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") message(STATUS "Features: ${SUPPORT_FEATURES}") # Clear list and collect SSL backends -unset(_items) -_add_if("Schannel" _ssl_enabled AND USE_SCHANNEL) -_add_if("OpenSSL" _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0) -_add_if("OpenSSL v3+" _ssl_enabled AND USE_OPENSSL AND NOT OPENSSL_VERSION VERSION_LESS 3.0.0) -_add_if("Secure Transport" _ssl_enabled AND USE_SECTRANSP) -_add_if("mbedTLS" _ssl_enabled AND USE_MBEDTLS) -_add_if("BearSSL" _ssl_enabled AND USE_BEARSSL) -_add_if("wolfSSL" _ssl_enabled AND USE_WOLFSSL) -_add_if("GnuTLS" _ssl_enabled AND USE_GNUTLS) -_add_if("rustls" _ssl_enabled AND USE_RUSTLS) +set(_items "") +curl_add_if("Schannel" _ssl_enabled AND USE_SCHANNEL) +curl_add_if("${_openssl}" _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0) +curl_add_if("${_openssl} v3+" _ssl_enabled AND USE_OPENSSL AND NOT OPENSSL_VERSION VERSION_LESS 3.0.0) +curl_add_if("Secure Transport" _ssl_enabled AND USE_SECTRANSP) +curl_add_if("mbedTLS" _ssl_enabled AND USE_MBEDTLS) +curl_add_if("BearSSL" _ssl_enabled AND USE_BEARSSL) +curl_add_if("wolfSSL" _ssl_enabled AND USE_WOLFSSL) +curl_add_if("GnuTLS" _ssl_enabled AND USE_GNUTLS) +curl_add_if("rustls" _ssl_enabled AND USE_RUSTLS) if(_items) if(NOT CMAKE_VERSION VERSION_LESS 3.13) @@ -2099,7 +2241,7 @@ if(NOT CURL_DISABLE_INSTALL) endforeach() # Avoid getting unnecessary -L options for known system directories. - unset(_sys_libdirs) + set(_sys_libdirs "") foreach(_libdir IN LISTS CMAKE_SYSTEM_PREFIX_PATH) if(_libdir MATCHES "/$") set(_libdir "${_libdir}lib") @@ -2124,7 +2266,7 @@ if(NOT CURL_DISABLE_INSTALL) endif() endforeach() - unset(_implicit_libs) + set(_implicit_libs "") if(NOT MINGW AND NOT UNIX) set(_implicit_libs ${CMAKE_C_IMPLICIT_LINK_LIBRARIES}) endif() @@ -2146,7 +2288,7 @@ if(NOT CURL_DISABLE_INSTALL) endif() if(_lib MATCHES "^-") # '-framework ' list(APPEND _ldflags "${_lib}") - elseif(_lib MATCHES ".*/.*") + elseif(_lib MATCHES "/") # This gets a bit more complex, because we want to specify the # directory separately, and only once per directory get_filename_component(_libdir ${_lib} DIRECTORY) @@ -2277,10 +2419,13 @@ if(NOT CURL_DISABLE_INSTALL) ${_generated_version_config}") # Consumed custom variables: + # CURLVERSION # LIB_SELECTED # TARGETS_EXPORT_NAME - # USE_OPENSSL - # HAVE_LIBZ + # USE_OPENSSL OPENSSL_VERSION_MAJOR + # HAVE_LIBZ ZLIB_VERSION_MAJOR + # CURL_SUPPORTED_FEATURES_LIST + # CURL_SUPPORTED_PROTOCOLS_LIST configure_package_config_file("CMake/curl-config.cmake.in" "${_project_config}" INSTALL_DESTINATION ${_install_cmake_dir} @@ -2295,15 +2440,6 @@ if(NOT CURL_DISABLE_INSTALL) install(FILES ${_version_config} ${_project_config} DESTINATION ${_install_cmake_dir}) - # Workaround for MSVS10 to avoid the Dialog Hell - # FIXME: This could be removed with future version of CMake. - if(MSVC_VERSION EQUAL 1600) - set(_curl_sln_filename "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln") - if(EXISTS "${_curl_sln_filename}") - file(APPEND "${_curl_sln_filename}" "\n# This should be regenerated!\n") - endif() - endif() - if(NOT TARGET curl_uninstall) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in" diff --git a/COPYING b/COPYING index d9e7e0b..3fa85eb 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2024, Daniel Stenberg, , and many +Copyright (c) 1996 - 2025, Daniel Stenberg, , and many contributors, see the THANKS file. All rights reserved. diff --git a/include/curl/curl.h b/include/curl/curl.h index 1883558..84cf5f2 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1959,10 +1959,10 @@ typedef enum { /* Set stream weight, 1 - 256 (default is 16) */ CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), - /* Set stream dependency on another CURL handle */ + /* Set stream dependency on another curl handle */ CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), - /* Set E-xclusive stream dependency on another CURL handle */ + /* Set E-xclusive stream dependency on another curl handle */ CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), /* Do not send any tftp option requests to the server */ @@ -2959,7 +2959,9 @@ typedef enum { CURLINFO_USED_PROXY = CURLINFO_LONG + 66, CURLINFO_POSTTRANSFER_TIME_T = CURLINFO_OFF_T + 67, CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68, - CURLINFO_LASTONE = 68 + CURLINFO_HTTPAUTH_USED = CURLINFO_LONG + 69, + CURLINFO_PROXYAUTH_USED = CURLINFO_LONG + 70, + CURLINFO_LASTONE = 70 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -3230,6 +3232,50 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) #define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) +/* + * NAME curl_easy_ssls_import() + * + * DESCRIPTION + * + * The curl_easy_ssls_import function adds a previously exported SSL session + * to the SSL session cache of the easy handle (or the underlying share). + */ +CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *handle, + const char *session_key, + const unsigned char *shmac, + size_t shmac_len, + const unsigned char *sdata, + size_t sdata_len); + +/* This is the curl_ssls_export_cb callback prototype. It + * is passed to curl_easy_ssls_export() to extract SSL sessions/tickets. */ +typedef CURLcode curl_ssls_export_cb(CURL *handle, + void *userptr, + const char *session_key, + const unsigned char *shmac, + size_t shmac_len, + const unsigned char *sdata, + size_t sdata_len, + curl_off_t valid_until, + int ietf_tls_id, + const char *alpn, + size_t earlydata_max); + +/* + * NAME curl_easy_ssls_export() + * + * DESCRIPTION + * + * The curl_easy_ssls_export function iterates over all SSL sessions stored + * in the easy handle (or underlying share) and invokes the passed + * callback. + * + */ +CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle, + curl_ssls_export_cb *export_fn, + void *userptr); + + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/include/curl/curlver.h b/include/curl/curlver.h index 6e97c13..376272e 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,13 +32,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.11.1-DEV" +#define LIBCURL_VERSION "8.12.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 11 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_MINOR 12 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will @@ -59,7 +59,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x080b01 +#define LIBCURL_VERSION_NUM 0x080c00 /* * This is the date and time when the full source package was created. The diff --git a/include/curl/easy.h b/include/curl/easy.h index 71b8dd4..56f8060 100644 --- a/include/curl/easy.h +++ b/include/curl/easy.h @@ -78,7 +78,7 @@ CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); * * DESCRIPTION * - * Re-initializes a CURL handle to the default values. This puts back the + * Re-initializes a curl handle to the default values. This puts back the * handle to the same state as it was in when it was just created. * * It does keep: live connections, the Session ID cache, the DNS cache and the diff --git a/include/curl/system.h b/include/curl/system.h index e5be256..820fe96 100644 --- a/include/curl/system.h +++ b/include/curl/system.h @@ -52,62 +52,24 @@ * */ -#if defined(__DJGPP__) || defined(__GO32__) -# if defined(__DJGPP__) && (__DJGPP__ > 1) -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# else -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# endif -# define CURL_TYPEOF_CURL_SOCKLEN_T int - -#elif defined(__SALFORDC__) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL +#if defined(__DJGPP__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__BORLANDC__) -# if (__BORLANDC__ < 0x520) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# else -# define CURL_TYPEOF_CURL_OFF_T __int64 -# define CURL_FORMAT_CURL_OFF_T "I64d" -# define CURL_FORMAT_CURL_OFF_TU "I64u" -# define CURL_SUFFIX_CURL_OFF_T i64 -# define CURL_SUFFIX_CURL_OFF_TU ui64 -# endif -# define CURL_TYPEOF_CURL_SOCKLEN_T int - -#elif defined(__TURBOC__) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__POCC__) -# if (__POCC__ < 280) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# elif defined(_MSC_VER) +# if defined(_MSC_VER) # define CURL_TYPEOF_CURL_OFF_T __int64 # define CURL_FORMAT_CURL_OFF_T "I64d" # define CURL_FORMAT_CURL_OFF_TU "I64u" @@ -159,15 +121,21 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int #elif defined(__TANDEM) -# if ! defined(__LP64) - /* Required for 32-bit NonStop builds only. */ -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# define CURL_TYPEOF_CURL_SOCKLEN_T int -# endif +# if !defined(__LP64) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int +# endif #elif defined(_WIN32_WCE) # define CURL_TYPEOF_CURL_OFF_T __int64 @@ -220,13 +188,7 @@ # define CURL_FORMAT_CURL_OFF_TU "llu" # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL -# elif defined(_LP64) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# else +# else /* _LP64 and default */ # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" @@ -239,22 +201,13 @@ #elif defined(__370__) # if defined(__IBMC__) || defined(__IBMCPP__) -# if defined(_ILP32) -# elif defined(_LP64) -# endif # if defined(_LONG_LONG) # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL -# elif defined(_LP64) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# else +# else /* _LP64 and default */ # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" @@ -349,24 +302,15 @@ #elif defined(_MSC_VER) # if (_MSC_VER >= 1800) # include -# define CURL_TYPEOF_CURL_OFF_T __int64 # define CURL_FORMAT_CURL_OFF_T PRId64 # define CURL_FORMAT_CURL_OFF_TU PRIu64 -# define CURL_SUFFIX_CURL_OFF_T i64 -# define CURL_SUFFIX_CURL_OFF_TU ui64 -# elif (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) -# define CURL_TYPEOF_CURL_OFF_T __int64 +# else # define CURL_FORMAT_CURL_OFF_T "I64d" # define CURL_FORMAT_CURL_OFF_TU "I64u" -# define CURL_SUFFIX_CURL_OFF_T i64 -# define CURL_SUFFIX_CURL_OFF_TU ui64 -# else -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL # endif +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 # define CURL_TYPEOF_CURL_SOCKLEN_T int /* ===================================== */ @@ -403,12 +347,12 @@ #else /* generic "safe guess" on old 32-bit style */ -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int #endif #ifdef _AIX @@ -462,7 +406,7 @@ #if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ - defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ + defined(__POCC__) || defined(__HIGHC__) || \ defined(__ILEC400__) /* This compiler is believed to have an ISO compatible preprocessor */ #define CURL_ISOCPP diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index aea8616..c1863b0 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -28,7 +28,7 @@ add_definitions("-DBUILDING_LIBCURL") 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") +curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") # DllMain is added later for DLL builds only. @@ -105,9 +105,7 @@ if(SHARE_LIB_OBJECT) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() if(CURL_HAS_LTO) - set_target_properties(${LIB_OBJECT} PROPERTIES - INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE - INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) + set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() target_include_directories(${LIB_OBJECT} INTERFACE @@ -138,9 +136,7 @@ if(BUILD_STATIC_LIBS) set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() if(CURL_HAS_LTO) - set_target_properties(${LIB_STATIC} PROPERTIES - INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE - INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) + set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() target_include_directories(${LIB_STATIC} INTERFACE @@ -177,9 +173,7 @@ if(BUILD_SHARED_LIBS) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() if(CURL_HAS_LTO) - set_target_properties(${LIB_SHARED} PROPERTIES - INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE - INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) + set_target_properties(${LIB_SHARED} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() target_include_directories(${LIB_SHARED} INTERFACE @@ -208,7 +202,7 @@ if(BUILD_SHARED_LIBS) 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") + curl_transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") math(EXPR _cmakesoname "${VERSIONCHANGE} - ${VERSIONDEL}") diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 1d3f69a..25c6df8 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -56,6 +56,8 @@ LIB_VTLS_CFILES = \ vtls/schannel_verify.c \ vtls/sectransp.c \ vtls/vtls.c \ + vtls/vtls_scache.c \ + vtls/vtls_spack.c \ vtls/wolfssl.c \ vtls/x509asn1.c @@ -74,6 +76,8 @@ LIB_VTLS_HFILES = \ vtls/sectransp.h \ vtls/vtls.h \ vtls/vtls_int.h \ + vtls/vtls_scache.h \ + vtls/vtls_spack.h \ vtls/wolfssl.h \ vtls/x509asn1.h @@ -112,7 +116,6 @@ LIB_CFILES = \ base64.c \ bufq.c \ bufref.c \ - c-hyper.c \ cf-h1-proxy.c \ cf-h2-proxy.c \ cf-haproxy.c \ @@ -177,6 +180,7 @@ LIB_CFILES = \ http_negotiate.c \ http_ntlm.c \ http_proxy.c \ + httpsrr.c \ idn.c \ if2ip.c \ imap.c \ @@ -223,6 +227,7 @@ LIB_CFILES = \ strcase.c \ strdup.c \ strerror.c \ + strparse.c \ strtok.c \ strtoofft.c \ system_win32.c \ @@ -245,7 +250,6 @@ LIB_HFILES = \ asyn.h \ bufq.h \ bufref.h \ - c-hyper.h \ cf-h1-proxy.h \ cf-h2-proxy.h \ cf-haproxy.h \ @@ -317,6 +321,7 @@ LIB_HFILES = \ http_negotiate.h \ http_ntlm.h \ http_proxy.h \ + httpsrr.h \ idn.h \ if2ip.h \ imap.h \ @@ -344,7 +349,9 @@ LIB_HFILES = \ select.h \ sendf.h \ setopt.h \ + setup-os400.h \ setup-vms.h \ + setup-win32.h \ share.h \ sigpipe.h \ slist.h \ @@ -358,6 +365,7 @@ LIB_HFILES = \ strcase.h \ strdup.h \ strerror.h \ + strparse.h \ strtok.h \ strtoofft.h \ system_win32.h \ diff --git a/lib/altsvc.c b/lib/altsvc.c index ea37b0a..095b3c1 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -40,6 +40,8 @@ #include "rename.h" #include "strdup.h" #include "inet_pton.h" +#include "strparse.h" +#include "connect.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -47,28 +49,12 @@ #include "memdebug.h" #define MAX_ALTSVC_LINE 4095 -#define MAX_ALTSVC_DATELENSTR "64" -#define MAX_ALTSVC_DATELEN 64 -#define MAX_ALTSVC_HOSTLENSTR "512" -#define MAX_ALTSVC_HOSTLEN 512 -#define MAX_ALTSVC_ALPNLENSTR "10" +#define MAX_ALTSVC_DATELEN 256 +#define MAX_ALTSVC_HOSTLEN 2048 #define MAX_ALTSVC_ALPNLEN 10 #define H3VERSION "h3" -static enum alpnid alpn2alpnid(char *name) -{ - if(strcasecompare(name, "h1")) - return ALPN_h1; - if(strcasecompare(name, "h2")) - return ALPN_h2; - if(strcasecompare(name, H3VERSION)) - return ALPN_h3; - if(strcasecompare(name, "http/1.1")) - return ALPN_h1; - return ALPN_none; /* unknown, probably rubbish input */ -} - /* Given the ALPN ID, return the name */ const char *Curl_alpnid2str(enum alpnid id) { @@ -93,33 +79,33 @@ static void altsvc_free(struct altsvc *as) } static struct altsvc *altsvc_createid(const char *srchost, + size_t hlen, const char *dsthost, size_t dlen, /* dsthost length */ enum alpnid srcalpnid, enum alpnid dstalpnid, - unsigned int srcport, - unsigned int dstport) + size_t srcport, + size_t dstport) { struct altsvc *as = calloc(1, sizeof(struct altsvc)); - size_t hlen; if(!as) return NULL; - hlen = strlen(srchost); DEBUGASSERT(hlen); DEBUGASSERT(dlen); - if(!hlen || !dlen) { + if(!hlen || !dlen) /* bad input */ - free(as); - return NULL; - } + goto error; if((hlen > 2) && srchost[0] == '[') { /* IPv6 address, strip off brackets */ srchost++; hlen -= 2; } - else if(srchost[hlen - 1] == '.') + else if(srchost[hlen - 1] == '.') { /* strip off trailing dot */ hlen--; + if(!hlen) + goto error; + } if((dlen > 2) && dsthost[0] == '[') { /* IPv6 address, strip off brackets */ dsthost++; @@ -136,8 +122,8 @@ static struct altsvc *altsvc_createid(const char *srchost, as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; - as->src.port = curlx_ultous(srcport); - as->dst.port = curlx_ultous(dstport); + as->src.port = (unsigned short)srcport; + as->dst.port = (unsigned short)dstport; return as; error: @@ -145,18 +131,19 @@ error: return NULL; } -static struct altsvc *altsvc_create(char *srchost, - char *dsthost, - char *srcalpn, - char *dstalpn, - unsigned int srcport, - unsigned int dstport) +static struct altsvc *altsvc_create(struct Curl_str *srchost, + struct Curl_str *dsthost, + struct Curl_str *srcalpn, + struct Curl_str *dstalpn, + size_t srcport, + size_t dstport) { - enum alpnid dstalpnid = alpn2alpnid(dstalpn); - enum alpnid srcalpnid = alpn2alpnid(srcalpn); + enum alpnid dstalpnid = Curl_alpn2alpnid(dstalpn->str, dstalpn->len); + enum alpnid srcalpnid = Curl_alpn2alpnid(srcalpn->str, srcalpn->len); if(!srcalpnid || !dstalpnid) return NULL; - return altsvc_createid(srchost, dsthost, strlen(dsthost), + return altsvc_createid(srchost->str, srchost->len, + dsthost->str, dsthost->len, srcalpnid, dstalpnid, srcport, dstport); } @@ -167,31 +154,50 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line) /* Example line: h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1 */ - char srchost[MAX_ALTSVC_HOSTLEN + 1]; - char dsthost[MAX_ALTSVC_HOSTLEN + 1]; - char srcalpn[MAX_ALTSVC_ALPNLEN + 1]; - char dstalpn[MAX_ALTSVC_ALPNLEN + 1]; - char date[MAX_ALTSVC_DATELEN + 1]; - unsigned int srcport; - unsigned int dstport; - unsigned int prio; - unsigned int persist; - int rc; - - rc = sscanf(line, - "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u " - "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u " - "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u", - srcalpn, srchost, &srcport, - dstalpn, dsthost, &dstport, - date, &persist, &prio); - if(9 == rc) { + struct Curl_str srchost; + struct Curl_str dsthost; + struct Curl_str srcalpn; + struct Curl_str dstalpn; + struct Curl_str date; + size_t srcport; + size_t dstport; + size_t persist; + size_t prio; + + if(Curl_str_word(&line, &srcalpn, MAX_ALTSVC_ALPNLEN) || + Curl_str_singlespace(&line) || + Curl_str_word(&line, &srchost, MAX_ALTSVC_HOSTLEN) || + Curl_str_singlespace(&line) || + Curl_str_number(&line, &srcport, 65535) || + Curl_str_singlespace(&line) || + Curl_str_word(&line, &dstalpn, MAX_ALTSVC_ALPNLEN) || + Curl_str_singlespace(&line) || + Curl_str_word(&line, &dsthost, MAX_ALTSVC_HOSTLEN) || + Curl_str_singlespace(&line) || + Curl_str_number(&line, &dstport, 65535) || + Curl_str_singlespace(&line) || + Curl_str_quotedword(&line, &date, MAX_ALTSVC_DATELEN) || + Curl_str_singlespace(&line) || + Curl_str_number(&line, &persist, 1) || + Curl_str_singlespace(&line) || + Curl_str_number(&line, &prio, 0) || + Curl_str_newline(&line)) + ; + else { struct altsvc *as; - time_t expires = Curl_getdate_capped(date); - as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport); + char dbuf[MAX_ALTSVC_DATELEN + 1]; + time_t expires; + + /* The date parser works on a null terminated string. The maximum length + is upheld by Curl_str_quotedword(). */ + memcpy(dbuf, date.str, date.len); + dbuf[date.len] = 0; + expires = Curl_getdate_capped(dbuf); + as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, srcport, + dstport); if(as) { as->expires = expires; - as->prio = prio; + as->prio = 0; /* not supported to just set zero */ as->persist = persist ? 1 : 0; Curl_llist_append(&asi->list, as, &as->node); } @@ -471,8 +477,6 @@ static time_t altsvc_debugtime(void *unused) #define time(x) altsvc_debugtime(x) #endif -#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') - /* * Curl_altsvc_parse() takes an incoming alt-svc response header and stores * the data correctly in the cache. @@ -495,6 +499,8 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, unsigned short dstport = srcport; /* the same by default */ CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); size_t entries = 0; + size_t alpnlen = strlen(alpnbuf); + size_t srchostlen = strlen(srchost); #ifdef CURL_DISABLE_VERBOSE_STRINGS (void)data; #endif @@ -515,7 +521,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, do { if(*p == '=') { /* [protocol]="[host][:port]" */ - enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */ + enum alpnid dstalpnid = Curl_alpn2alpnid(alpnbuf, alpnlen); p++; if(*p == '\"') { const char *dsthost = ""; @@ -633,13 +639,18 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, this is the first entry of the line. */ altsvc_flush(asi, srcalpnid, srchost, srcport); - as = altsvc_createid(srchost, dsthost, dstlen, + as = altsvc_createid(srchost, srchostlen, + dsthost, dstlen, srcalpnid, dstalpnid, srcport, dstport); if(as) { - /* The expires time also needs to take the Age: value (if any) into - account. [See RFC 7838 section 3.1] */ - as->expires = maxage + time(NULL); + time_t secs = time(NULL); + /* The expires time also needs to take the Age: value (if any) + into account. [See RFC 7838 section 3.1] */ + if(maxage > (TIME_T_MAX - secs)) + as->expires = TIME_T_MAX; + else + as->expires = maxage + secs; as->persist = persist; Curl_llist_append(&asi->list, as, &as->node); infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport, diff --git a/lib/altsvc.h b/lib/altsvc.h index 48999ef..5f94f83 100644 --- a/lib/altsvc.h +++ b/lib/altsvc.h @@ -29,13 +29,6 @@ #include #include "llist.h" -enum alpnid { - ALPN_none = 0, - ALPN_h1 = CURLALTSVC_H1, - ALPN_h2 = CURLALTSVC_H2, - ALPN_h3 = CURLALTSVC_H3 -}; - struct althost { char *host; unsigned short port; diff --git a/lib/amigaos.c b/lib/amigaos.c index 1321c53..c4872f2 100644 --- a/lib/amigaos.c +++ b/lib/amigaos.c @@ -196,12 +196,11 @@ int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, */ struct Library *SocketBase = NULL; -extern int errno, h_errno; #ifdef __libnix__ void __request(const char *msg); #else -# define __request(msg) Printf(msg "\n\a") +# define __request(msg) Printf((const unsigned char *)(msg "\n\a"), 0) #endif void Curl_amiga_cleanup(void) @@ -215,7 +214,7 @@ void Curl_amiga_cleanup(void) CURLcode Curl_amiga_init(void) { if(!SocketBase) - SocketBase = OpenLibrary("bsdsocket.library", 4); + SocketBase = OpenLibrary((const unsigned char *)"bsdsocket.library", 4); if(!SocketBase) { __request("No TCP/IP Stack running!"); diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index ae436f2..38dc729 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -24,14 +24,14 @@ #include "curl_setup.h" +#ifdef USE_ARES + /*********************************************************************** * Only for ares-enabled builds * And only for functions that fulfill the asynch resolver backend API * as defined in asyn.h, nothing else belongs in this file! **********************************************************************/ -#ifdef CURLRES_ARES - #include #ifdef HAVE_NETINET_IN_H #include @@ -59,15 +59,101 @@ #include "select.h" #include "progress.h" #include "timediff.h" +#include "httpsrr.h" +#include "strdup.h" -#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ - defined(_WIN32) -# define CARES_STATICLIB -#endif #include #include /* really old c-ares did not include this by itself */ +/* + * Curl_ares_getsock() is called when the outside world (using + * curl_multi_fdset()) wants to get our fd_set setup and we are talking with + * ares. The caller must make sure that this function is only called when we + * have a working ares channel. + * + * Returns: sockets-in-use-bitmap + */ + +int Curl_ares_getsock(struct Curl_easy *data, + ares_channel channel, + curl_socket_t *socks) +{ + struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; + struct timeval timebuf; + int max = ares_getsock(channel, + (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); + struct timeval *timeout = ares_timeout(channel, &maxtime, &timebuf); + timediff_t milli = curlx_tvtoms(timeout); + Curl_expire(data, milli, EXPIRE_ASYNC_NAME); + return max; +} + +/* + * Curl_ares_perform() + * + * 1) Ask ares what sockets it currently plays with, then + * 2) wait for the timeout period to check for action on ares' sockets. + * 3) tell ares to act on all the sockets marked as "with action" + * + * return number of sockets it worked on, or -1 on error + */ + +int Curl_ares_perform(ares_channel channel, + timediff_t timeout_ms) +{ + int nfds; + int bitmask; + ares_socket_t socks[ARES_GETSOCK_MAXNUM]; + struct pollfd pfd[ARES_GETSOCK_MAXNUM]; + int i; + int num = 0; + + bitmask = ares_getsock(channel, socks, ARES_GETSOCK_MAXNUM); + + for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) { + pfd[i].events = 0; + pfd[i].revents = 0; + if(ARES_GETSOCK_READABLE(bitmask, i)) { + pfd[i].fd = socks[i]; + pfd[i].events |= POLLRDNORM|POLLIN; + } + if(ARES_GETSOCK_WRITABLE(bitmask, i)) { + pfd[i].fd = socks[i]; + pfd[i].events |= POLLWRNORM|POLLOUT; + } + if(pfd[i].events) + num++; + else + break; + } + + if(num) { + nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms); + if(nfds < 0) + return -1; + } + else + nfds = 0; + + if(!nfds) + /* Call ares_process() unconditionally here, even if we simply timed out + above, as otherwise the ares name resolve will not timeout! */ + ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); + else { + /* move through the descriptors and ask for processing on them */ + for(i = 0; i < num; i++) + ares_process_fd(channel, + (pfd[i].revents & (POLLRDNORM|POLLIN)) ? + pfd[i].fd : ARES_SOCKET_BAD, + (pfd[i].revents & (POLLWRNORM|POLLOUT)) ? + pfd[i].fd : ARES_SOCKET_BAD); + } + return nfds; +} + +#ifdef CURLRES_ARES + #if ARES_VERSION >= 0x010500 /* c-ares 1.5.0 or later, the callback proto is modified */ #define HAVE_CARES_CALLBACK_TIMEOUTS 1 @@ -93,22 +179,19 @@ #define HAVE_CARES_GETADDRINFO 1 #endif +#if ARES_VERSION >= 0x011c00 +/* 1.28.0 and later have ares_query_dnsrec */ +#define HAVE_ARES_QUERY_DNSREC 1 +#ifdef USE_HTTPSRR +#define USE_HTTPSRR_ARES 1 +#endif +#endif + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -struct thread_data { - int num_pending; /* number of outstanding c-ares requests */ - struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares - parts */ - int last_status; -#ifndef HAVE_CARES_GETADDRINFO - struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ -#endif - char hostname[1]; -}; - /* How long we are willing to wait for additional parallel responses after obtaining a "definitive" one. For old c-ares without getaddrinfo. @@ -280,89 +363,13 @@ static void destroy_async_data(struct Curl_async *async) /* * Curl_resolver_getsock() is called when someone from the outside world - * (using curl_multi_fdset()) wants to get our fd_set setup and we are talking - * with ares. The caller must make sure that this function is only called when - * we have a working ares channel. - * - * Returns: sockets-in-use-bitmap - */ - -int Curl_resolver_getsock(struct Curl_easy *data, - curl_socket_t *socks) -{ - struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; - struct timeval timebuf; - int max = ares_getsock((ares_channel)data->state.async.resolver, - (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); - struct timeval *timeout = - ares_timeout((ares_channel)data->state.async.resolver, &maxtime, &timebuf); - timediff_t milli = curlx_tvtoms(timeout); - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); - return max; -} - -/* - * waitperform() - * - * 1) Ask ares what sockets it currently plays with, then - * 2) wait for the timeout period to check for action on ares' sockets. - * 3) tell ares to act on all the sockets marked as "with action" - * - * return number of sockets it worked on, or -1 on error + * (using curl_multi_fdset()) wants to get our fd_set setup. */ -static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) +int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks) { - int nfds; - int bitmask; - ares_socket_t socks[ARES_GETSOCK_MAXNUM]; - struct pollfd pfd[ARES_GETSOCK_MAXNUM]; - int i; - int num = 0; - - bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks, - ARES_GETSOCK_MAXNUM); - - for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) { - pfd[i].events = 0; - pfd[i].revents = 0; - if(ARES_GETSOCK_READABLE(bitmask, i)) { - pfd[i].fd = socks[i]; - pfd[i].events |= POLLRDNORM|POLLIN; - } - if(ARES_GETSOCK_WRITABLE(bitmask, i)) { - pfd[i].fd = socks[i]; - pfd[i].events |= POLLWRNORM|POLLOUT; - } - if(pfd[i].events) - num++; - else - break; - } - - if(num) { - nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms); - if(nfds < 0) - return -1; - } - else - nfds = 0; - - if(!nfds) - /* Call ares_process() unconditionally here, even if we simply timed out - above, as otherwise the ares name resolve will not timeout! */ - ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD, - ARES_SOCKET_BAD); - else { - /* move through the descriptors and ask for processing on them */ - for(i = 0; i < num; i++) - ares_process_fd((ares_channel)data->state.async.resolver, - (pfd[i].revents & (POLLRDNORM|POLLIN)) ? - pfd[i].fd : ARES_SOCKET_BAD, - (pfd[i].revents & (POLLWRNORM|POLLOUT)) ? - pfd[i].fd : ARES_SOCKET_BAD); - } - return nfds; + return Curl_ares_getsock(data, (ares_channel)data->state.async.resolver, + socks); } /* @@ -381,7 +388,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, DEBUGASSERT(dns); *dns = NULL; - if(waitperform(data, 0) < 0) + if(Curl_ares_perform((ares_channel)data->state.async.resolver, 0) < 0) return CURLE_UNRECOVERABLE_POLL; #ifndef HAVE_CARES_GETADDRINFO @@ -417,8 +424,19 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, if(!data->state.async.dns) result = Curl_resolver_error(data); - else + else { *dns = data->state.async.dns; +#ifdef USE_HTTPSRR_ARES + { + struct Curl_https_rrinfo *lhrr = + Curl_memdup(&res->hinfo, sizeof(struct Curl_https_rrinfo)); + if(!lhrr) + result = CURLE_OUT_OF_MEMORY; + else + (*dns)->hinfo = lhrr; + } +#endif + } destroy_async_data(&data->state.async); } @@ -481,7 +499,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, else timeout_ms = 1000; - if(waitperform(data, timeout_ms) < 0) + if(Curl_ares_perform((ares_channel)data->state.async.resolver, + timeout_ms) < 0) return CURLE_UNRECOVERABLE_POLL; result = Curl_resolver_is_resolved(data, entry); @@ -745,6 +764,7 @@ static void addrinfo_cb(void *arg, int status, int timeouts, } #endif + /* * Curl_resolver_getaddrinfo() - when using ares * @@ -827,6 +847,16 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, query_completed_cb, data); } #endif +#ifdef USE_HTTPSRR_ARES + { + res->num_pending++; /* one more */ + memset(&res->hinfo, 0, sizeof(struct Curl_https_rrinfo)); + ares_query_dnsrec((ares_channel)data->state.async.resolver, + hostname, ARES_CLASS_IN, + ARES_REC_TYPE_HTTPS, + Curl_dnsrec_done_cb, data, NULL); + } +#endif *waitp = 1; /* expect asynchronous response */ } return NULL; /* no struct yet */ @@ -838,14 +868,26 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, CURLcode result = CURLE_NOT_BUILT_IN; int ares_result; - /* If server is NULL or empty, this would purge all DNS servers - * from ares library, which will cause any and all queries to fail. - * So, just return OK if none are configured and do not actually make - * any changes to c-ares. This lets c-ares use its defaults, which - * it gets from the OS (for instance from /etc/resolv.conf on Linux). + /* If server is NULL, this purges all DNS servers from c-ares. Reset it to + * default. */ - if(!(servers && servers[0])) - return CURLE_OK; + if(!servers) { + Curl_resolver_cleanup(data->state.async.resolver); + result = Curl_resolver_init(data, &data->state.async.resolver); + if(!result) { + /* this now needs to restore the other options set to c-ares */ + if(data->set.str[STRING_DNS_INTERFACE]) + (void)Curl_set_dns_interface(data, + data->set.str[STRING_DNS_INTERFACE]); + if(data->set.str[STRING_DNS_LOCAL_IP4]) + (void)Curl_set_dns_local_ip4(data, + data->set.str[STRING_DNS_LOCAL_IP4]); + if(data->set.str[STRING_DNS_LOCAL_IP6]) + (void)Curl_set_dns_local_ip6(data, + data->set.str[STRING_DNS_LOCAL_IP6]); + } + return result; + } #ifdef HAVE_CARES_SERVERS_CSV #ifdef HAVE_CARES_PORTS_CSV @@ -947,3 +989,5 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, #endif } #endif /* CURLRES_ARES */ + +#endif /* USE_ARES */ diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c index a58e4b7..b8c3049 100644 --- a/lib/asyn-thread.c +++ b/lib/asyn-thread.c @@ -64,6 +64,15 @@ #include "inet_ntop.h" #include "curl_threads.h" #include "connect.h" +#include "strdup.h" + +#ifdef USE_ARES +#include +#ifdef USE_HTTPSRR +#define USE_HTTPSRR_ARES 1 /* the combo */ +#endif +#endif + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -145,32 +154,6 @@ static bool init_resolve_thread(struct Curl_easy *data, const struct addrinfo *hints); -/* Data for synchronization between resolver thread and its parent */ -struct thread_sync_data { - curl_mutex_t *mtx; - int done; - int port; - char *hostname; /* hostname to resolve, Curl_async.hostname - duplicate */ -#ifndef CURL_DISABLE_SOCKETPAIR - struct Curl_easy *data; - curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */ -#endif - int sock_error; - struct Curl_addrinfo *res; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints; -#endif - struct thread_data *td; /* for thread-self cleanup */ -}; - -struct thread_data { - curl_thread_t thread_hnd; - unsigned int poll_interval; - timediff_t interval_end; - struct thread_sync_data tsd; -}; - static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data) { return &(data->state.async.tdata->tsd); @@ -195,10 +178,12 @@ void destroy_thread_sync_data(struct thread_sync_data *tsd) * close one end of the socket pair (may be done in resolver thread); * the other end (for reading) is always closed in the parent thread. */ +#ifndef USE_EVENTFD if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { wakeup_close(tsd->sock_pair[1]); } #endif +#endif memset(tsd, 0, sizeof(*tsd)); } @@ -218,7 +203,7 @@ int init_thread_sync_data(struct thread_data *td, /* Treat the request as done until the thread actually starts so any early * cleanup gets done properly. */ - tsd->done = 1; + tsd->done = TRUE; #ifdef HAVE_GETADDRINFO DEBUGASSERT(hints); tsd->hints = *hints; @@ -341,7 +326,7 @@ CURL_STDCALL getaddrinfo_thread(void *arg) } } #endif - tsd->done = 1; + tsd->done = TRUE; Curl_mutex_release(tsd->mtx); } @@ -380,7 +365,7 @@ CURL_STDCALL gethostbyname_thread(void *arg) free(td); } else { - tsd->done = 1; + tsd->done = TRUE; Curl_mutex_release(tsd->mtx); } @@ -396,19 +381,22 @@ static void destroy_async_data(struct Curl_async *async) { if(async->tdata) { struct thread_data *td = async->tdata; - int done; + bool done; #ifndef CURL_DISABLE_SOCKETPAIR curl_socket_t sock_rd = td->tsd.sock_pair[0]; struct Curl_easy *data = td->tsd.data; #endif +#ifdef USE_HTTPSRR_ARES + ares_destroy(data->state.async.tdata->channel); +#endif /* * if the thread is still blocking in the resolve syscall, detach it and * let the thread do the cleanup... */ Curl_mutex_acquire(td->tsd.mtx); done = td->tsd.done; - td->tsd.done = 1; + td->tsd.done = TRUE; Curl_mutex_release(td->tsd.mtx); if(!done) { @@ -437,6 +425,24 @@ static void destroy_async_data(struct Curl_async *async) async->hostname = NULL; } +#ifdef USE_HTTPSRR_ARES +static CURLcode resolve_httpsrr(struct Curl_easy *data, + struct Curl_async *asp) +{ + int status = ares_init_options(&asp->tdata->channel, NULL, 0); + if(status != ARES_SUCCESS) + return CURLE_FAILED_INIT; + + memset(&asp->tdata->hinfo, 0, sizeof(struct Curl_https_rrinfo)); + ares_query_dnsrec(asp->tdata->channel, + asp->hostname, ARES_CLASS_IN, + ARES_REC_TYPE_HTTPS, + Curl_dnsrec_done_cb, data, NULL); + + return CURLE_OK; +} +#endif + /* * init_resolve_thread() starts a new thread that performs the actual * resolve. This function returns before the resolve is done. @@ -472,8 +478,8 @@ static bool init_resolve_thread(struct Curl_easy *data, if(!asp->hostname) goto err_exit; - /* The thread will set this to 1 when complete. */ - td->tsd.done = 0; + /* The thread will set this TRUE when complete. */ + td->tsd.done = FALSE; #ifdef HAVE_GETADDRINFO td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); @@ -483,11 +489,14 @@ static bool init_resolve_thread(struct Curl_easy *data, if(td->thread_hnd == curl_thread_t_null) { /* The thread never started, so mark it as done here for proper cleanup. */ - td->tsd.done = 1; + td->tsd.done = TRUE; err = errno; goto err_exit; } - +#ifdef USE_HTTPSRR_ARES + if(resolve_httpsrr(data, asp)) + goto err_exit; +#endif return TRUE; err_exit: @@ -585,7 +594,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **entry) { struct thread_data *td = data->state.async.tdata; - int done = 0; + bool done = FALSE; DEBUGASSERT(entry); *entry = NULL; @@ -594,6 +603,10 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, DEBUGASSERT(td); return CURLE_COULDNT_RESOLVE_HOST; } +#ifdef USE_HTTPSRR_ARES + if(Curl_ares_perform(data->state.async.tdata->channel, 0) < 0) + return CURLE_UNRECOVERABLE_POLL; +#endif Curl_mutex_acquire(td->tsd.mtx); done = td->tsd.done; @@ -607,6 +620,17 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, destroy_async_data(&data->state.async); return result; } +#ifdef USE_HTTPSRR_ARES + { + struct Curl_https_rrinfo *lhrr = + Curl_memdup(&td->hinfo, sizeof(struct Curl_https_rrinfo)); + if(!lhrr) { + destroy_async_data(&data->state.async); + return CURLE_OUT_OF_MEMORY; + } + data->state.async.dns->hinfo = lhrr; + } +#endif destroy_async_data(&data->state.async); *entry = data->state.async.dns; } @@ -641,18 +665,28 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks) timediff_t milli; timediff_t ms; struct resdata *reslv = (struct resdata *)data->state.async.resolver; + int socketi = 0; #ifndef CURL_DISABLE_SOCKETPAIR struct thread_data *td = data->state.async.tdata; #else (void)socks; #endif +#ifdef USE_HTTPSRR_ARES + if(data->state.async.tdata) { + ret_val = Curl_ares_getsock(data, data->state.async.tdata->channel, socks); + for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++) + if(!ARES_GETSOCK_READABLE(ret_val, socketi) && + !ARES_GETSOCK_WRITABLE(ret_val, socketi)) + break; + } +#endif #ifndef CURL_DISABLE_SOCKETPAIR if(td) { /* return read fd to client for polling the DNS resolution status */ - socks[0] = td->tsd.sock_pair[0]; + socks[socketi] = td->tsd.sock_pair[0]; td->tsd.data = data; - ret_val = GETSOCK_READSOCK(0); + ret_val = GETSOCK_READSOCK(socketi); } else { #endif diff --git a/lib/asyn.h b/lib/asyn.h index 0ff2048..c674541 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -26,6 +26,7 @@ #include "curl_setup.h" #include "curl_addrinfo.h" +#include "httpsrr.h" struct addrinfo; struct hostent; @@ -33,6 +34,69 @@ struct Curl_easy; struct connectdata; struct Curl_dns_entry; +#ifdef CURLRES_THREADED +#include "curl_threads.h" + +/* Data for synchronization between resolver thread and its parent */ +struct thread_sync_data { + curl_mutex_t *mtx; + bool done; + int port; + char *hostname; /* hostname to resolve, Curl_async.hostname + duplicate */ +#ifndef CURL_DISABLE_SOCKETPAIR + struct Curl_easy *data; + curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */ +#endif + int sock_error; + struct Curl_addrinfo *res; +#ifdef HAVE_GETADDRINFO + struct addrinfo hints; +#endif + struct thread_data *td; /* for thread-self cleanup */ +}; + +struct thread_data { + curl_thread_t thread_hnd; + unsigned int poll_interval; + timediff_t interval_end; + struct thread_sync_data tsd; +#if defined(USE_HTTPSRR) && defined(USE_ARES) + struct Curl_https_rrinfo hinfo; + ares_channel channel; +#endif +}; + +#elif defined(CURLRES_ARES) /* CURLRES_THREADED */ + +struct thread_data { + int num_pending; /* number of outstanding c-ares requests */ + struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares + parts */ + int last_status; +#ifndef HAVE_CARES_GETADDRINFO + struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ +#endif +#ifdef USE_HTTPSRR + struct Curl_https_rrinfo hinfo; +#endif + char hostname[1]; +}; + +#endif /* CURLRES_ARES */ + +#ifdef USE_ARES +#include + +/* for HTTPS RR purposes as well */ +int Curl_ares_getsock(struct Curl_easy *data, + ares_channel channel, + curl_socket_t *socks); +int Curl_ares_perform(ares_channel channel, + timediff_t timeout_ms); +#endif + + /* * This header defines all functions in the internal asynch resolver interface. * All asynch resolvers need to provide these functions. diff --git a/lib/bufq.c b/lib/bufq.c index 547d4d3..724d62f 100644 --- a/lib/bufq.c +++ b/lib/bufq.c @@ -45,11 +45,6 @@ static size_t chunk_len(const struct buf_chunk *chunk) return chunk->w_offset - chunk->r_offset; } -static size_t chunk_space(const struct buf_chunk *chunk) -{ - return chunk->dlen - chunk->w_offset; -} - static void chunk_reset(struct buf_chunk *chunk) { chunk->next = NULL; @@ -287,24 +282,6 @@ size_t Curl_bufq_len(const struct bufq *q) return len; } -size_t Curl_bufq_space(const struct bufq *q) -{ - size_t space = 0; - if(q->tail) - space += chunk_space(q->tail); - if(q->spare) { - struct buf_chunk *chunk = q->spare; - while(chunk) { - space += chunk->dlen; - chunk = chunk->next; - } - } - if(q->chunk_count < q->max_chunks) { - space += (q->max_chunks - q->chunk_count) * q->chunk_size; - } - return space; -} - bool Curl_bufq_is_empty(const struct bufq *q) { return !q->head || chunk_is_empty(q->head); diff --git a/lib/bufq.h b/lib/bufq.h index ec41564..60059de 100644 --- a/lib/bufq.h +++ b/lib/bufq.h @@ -151,14 +151,6 @@ void Curl_bufq_free(struct bufq *q); size_t Curl_bufq_len(const struct bufq *q); /** - * Return the total amount of free space in the queue. - * The returned length is the number of bytes that can - * be expected to be written successfully to the bufq, - * providing no memory allocations fail. - */ -size_t Curl_bufq_space(const struct bufq *q); - -/** * Returns TRUE iff there is no data in the buffer queue. */ bool Curl_bufq_is_empty(const struct bufq *q); diff --git a/lib/c-hyper.c b/lib/c-hyper.c deleted file mode 100644 index 2b8eb95..0000000 --- a/lib/c-hyper.c +++ /dev/null @@ -1,1254 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -/* Curl's integration with Hyper. This replaces certain functions in http.c, - * based on configuration #defines. This implementation supports HTTP/1.1 but - * not HTTP/2. - */ -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) - -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#ifdef HAVE_NETDB_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef HAVE_NET_IF_H -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif - -#ifdef HAVE_SYS_PARAM_H -#include -#endif - -#include -#include "urldata.h" -#include "cfilters.h" -#include "sendf.h" -#include "headers.h" -#include "transfer.h" -#include "multiif.h" -#include "progress.h" -#include "content_encoding.h" -#include "ws.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -static CURLcode cr_hyper_add(struct Curl_easy *data); - -typedef enum { - USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */ - USERDATA_RESP_BODY -} userdata_t; - -size_t Curl_hyper_recv(void *userp, hyper_context *ctx, - uint8_t *buf, size_t buflen) -{ - struct hyp_io_ctx *io_ctx = userp; - struct Curl_easy *data = io_ctx->data; - struct connectdata *conn = data->conn; - CURLcode result; - ssize_t nread; - DEBUGASSERT(conn); - (void)ctx; - - DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen)); - result = Curl_conn_recv(data, io_ctx->sockindex, - (char *)buf, buflen, &nread); - if(result == CURLE_AGAIN) { - /* would block, register interest */ - DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen)); - if(data->hyp.read_waker) - hyper_waker_free(data->hyp.read_waker); - data->hyp.read_waker = hyper_context_waker(ctx); - if(!data->hyp.read_waker) { - failf(data, "Couldn't make the read hyper_context_waker"); - return HYPER_IO_ERROR; - } - return HYPER_IO_PENDING; - } - else if(result) { - failf(data, "Curl_read failed"); - return HYPER_IO_ERROR; - } - DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> %zd", buflen, nread)); - return (size_t)nread; -} - -size_t Curl_hyper_send(void *userp, hyper_context *ctx, - const uint8_t *buf, size_t buflen) -{ - struct hyp_io_ctx *io_ctx = userp; - struct Curl_easy *data = io_ctx->data; - CURLcode result; - size_t nwrote; - - DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen)); - result = Curl_conn_send(data, io_ctx->sockindex, - (void *)buf, buflen, FALSE, &nwrote); - if(result == CURLE_AGAIN) { - DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen)); - /* would block, register interest */ - if(data->hyp.write_waker) - hyper_waker_free(data->hyp.write_waker); - data->hyp.write_waker = hyper_context_waker(ctx); - if(!data->hyp.write_waker) { - failf(data, "Couldn't make the write hyper_context_waker"); - return HYPER_IO_ERROR; - } - return HYPER_IO_PENDING; - } - else if(result) { - failf(data, "Curl_write failed"); - return HYPER_IO_ERROR; - } - DEBUGF(infof(data, "Curl_hyper_send(%zu) -> %zd", buflen, nwrote)); - return (size_t)nwrote; -} - -static int hyper_each_header(void *userdata, - const uint8_t *name, - size_t name_len, - const uint8_t *value, - size_t value_len) -{ - struct Curl_easy *data = (struct Curl_easy *)userdata; - size_t len; - char *headp; - CURLcode result; - int writetype; - - if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) { - failf(data, "Too long response header"); - data->state.hresult = CURLE_TOO_LARGE; - return HYPER_ITER_BREAK; - } - - Curl_dyn_reset(&data->state.headerb); - if(name_len) { - if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n", - (int) name_len, name, (int) value_len, value)) - return HYPER_ITER_BREAK; - } - else { - if(Curl_dyn_addn(&data->state.headerb, STRCONST("\r\n"))) - return HYPER_ITER_BREAK; - } - len = Curl_dyn_len(&data->state.headerb); - headp = Curl_dyn_ptr(&data->state.headerb); - - result = Curl_http_header(data, headp, len); - if(result) { - data->state.hresult = result; - return HYPER_ITER_BREAK; - } - - Curl_debug(data, CURLINFO_HEADER_IN, headp, len); - - writetype = CLIENTWRITE_HEADER; - if(data->state.hconnect) - writetype |= CLIENTWRITE_CONNECT; - if(data->req.httpcode/100 == 1) - writetype |= CLIENTWRITE_1XX; - result = Curl_client_write(data, writetype, headp, len); - if(result) { - data->state.hresult = CURLE_ABORTED_BY_CALLBACK; - return HYPER_ITER_BREAK; - } - - result = Curl_bump_headersize(data, len, FALSE); - if(result) { - data->state.hresult = result; - return HYPER_ITER_BREAK; - } - return HYPER_ITER_CONTINUE; -} - -static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) -{ - char *buf = (char *)hyper_buf_bytes(chunk); - size_t len = hyper_buf_len(chunk); - struct Curl_easy *data = (struct Curl_easy *)userdata; - struct SingleRequest *k = &data->req; - CURLcode result = CURLE_OK; - - if(!k->bodywritten) { -#if defined(USE_NTLM) - struct connectdata *conn = data->conn; - if(conn->bits.close && - (((data->req.httpcode == 401) && - (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || - ((data->req.httpcode == 407) && - (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { - infof(data, "Connection closed while negotiating NTLM"); - data->state.authproblem = TRUE; - Curl_safefree(data->req.newurl); - } -#endif - if(Curl_http_exp100_is_selected(data)) { - if(data->req.httpcode < 400) { - Curl_http_exp100_got100(data); - if(data->hyp.send_body_waker) { - hyper_waker_wake(data->hyp.send_body_waker); - data->hyp.send_body_waker = NULL; - } - } - else { /* >= 4xx */ - Curl_req_abort_sending(data); - } - } - if(data->state.hconnect && (data->req.httpcode/100 != 2) && - data->state.authproxy.done) { - data->req.done = TRUE; - result = CURLE_OK; - } - else - result = Curl_http_firstwrite(data); - if(result || data->req.done) { - infof(data, "Return early from hyper_body_chunk"); - data->state.hresult = result; - return HYPER_ITER_BREAK; - } - } - result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len); - - if(result) { - data->state.hresult = result; - return HYPER_ITER_BREAK; - } - - return HYPER_ITER_CONTINUE; -} - -/* - * Hyper does not consider the status line, the first line in an HTTP/1 - * response, to be a header. The libcurl API does. This function sends the - * status line in the header callback. */ -static CURLcode status_line(struct Curl_easy *data, - struct connectdata *conn, - uint16_t http_status, - int http_version, - const uint8_t *reason, size_t rlen) -{ - CURLcode result; - size_t len; - const char *vstr; - int writetype; - vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" : - (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0"); - - /* We need to set 'httpcodeq' for functions that check the response code in - a single place. */ - data->req.httpcode = http_status; - data->req.httpversion = http_version == HYPER_HTTP_VERSION_1_1 ? 11 : - (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10); - if(data->state.hconnect) - /* CONNECT */ - data->info.httpproxycode = http_status; - else { - conn->httpversion = (unsigned char)data->req.httpversion; - if(http_version == HYPER_HTTP_VERSION_1_0) - data->state.httpwant = CURL_HTTP_VERSION_1_0; - - result = Curl_http_statusline(data, conn); - if(result) - return result; - } - - Curl_dyn_reset(&data->state.headerb); - - result = Curl_dyn_addf(&data->state.headerb, "HTTP/%s %03d %.*s\r\n", - vstr, - (int)http_status, - (int)rlen, reason); - if(result) - return result; - len = Curl_dyn_len(&data->state.headerb); - Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb), - len); - - writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS; - if(data->state.hconnect) - writetype |= CLIENTWRITE_CONNECT; - result = Curl_client_write(data, writetype, - Curl_dyn_ptr(&data->state.headerb), len); - if(result) - return result; - - result = Curl_bump_headersize(data, len, FALSE); - return result; -} - -/* - * Hyper does not pass on the last empty response header. The libcurl API - * does. This function sends an empty header in the header callback. - */ -static CURLcode empty_header(struct Curl_easy *data) -{ - CURLcode result = Curl_http_size(data); - if(!result) { - result = hyper_each_header(data, NULL, 0, NULL, 0) ? - CURLE_WRITE_ERROR : CURLE_OK; - if(result) - failf(data, "hyperstream: could not pass blank header"); - /* Hyper does chunked decoding itself. If it was added during - * response header processing, remove it again. */ - Curl_cwriter_remove_by_name(data, "chunked"); - } - return result; -} - -CURLcode Curl_hyper_stream(struct Curl_easy *data, - struct connectdata *conn, - int *didwhat, - int select_res) -{ - hyper_response *resp = NULL; - uint16_t http_status; - int http_version; - hyper_headers *headers = NULL; - hyper_body *resp_body = NULL; - struct hyptransfer *h = &data->hyp; - hyper_task *task; - hyper_task *foreach; - const uint8_t *reasonp; - size_t reason_len; - CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - (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; - } - - if(select_res & CURL_CSELECT_IN) { - if(h->read_waker) - hyper_waker_wake(h->read_waker); - h->read_waker = NULL; - } - if(select_res & CURL_CSELECT_OUT) { - if(h->write_waker) - hyper_waker_wake(h->write_waker); - h->write_waker = NULL; - } - - while(1) { - hyper_task_return_type t; - task = hyper_executor_poll(h->exec); - if(!task) { - *didwhat = KEEP_RECV; - break; - } - t = hyper_task_type(task); - if(t == HYPER_TASK_ERROR) { - hyper_error *hypererr = hyper_task_value(task); - hyper_task_free(task); - if(data->state.hresult) { - /* override Hyper's view, might not even be an error */ - result = data->state.hresult; - infof(data, "hyperstream is done (by early callback)"); - } - else { - uint8_t errbuf[256]; - size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); - hyper_code code = hyper_error_code(hypererr); - failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf); - switch(code) { - case HYPERE_ABORTED_BY_CALLBACK: - result = CURLE_OK; - goto out; - case HYPERE_UNEXPECTED_EOF: - if(!data->req.bytecount) - result = CURLE_GOT_NOTHING; - else - result = CURLE_RECV_ERROR; - 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 */ - goto out; - default: - result = CURLE_RECV_ERROR; - goto out; - } - } - data->req.done = TRUE; - hyper_error_free(hypererr); - break; - } - else if(t == HYPER_TASK_EMPTY) { - void *userdata = hyper_task_userdata(task); - hyper_task_free(task); - if(userdata == (void *)USERDATA_RESP_BODY) { - /* end of transfer */ - data->req.done = TRUE; - infof(data, "hyperstream is done"); - if(!k->bodywritten) { - /* hyper does not always call the body write callback */ - result = Curl_http_firstwrite(data); - } - break; - } - 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); - - *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); - - 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) - goto out; - - 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; - goto out; - } - - result = empty_header(data); - if(result) - goto out; - - k->deductheadercount = - (100 <= http_status && 199 >= http_status) ? k->headerbytecount : 0; -#ifndef CURL_DISABLE_WEBSOCKETS - 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) - goto out; - - 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; - } - else { - DEBUGF(infof(data, "hyper: unhandled tasktype %x", t)); - } - } /* 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; -} - -static CURLcode debug_request(struct Curl_easy *data, - const char *method, - const char *path) -{ - char *req = aprintf("%s %s HTTP/1.1\r\n", method, path); - if(!req) - return CURLE_OUT_OF_MEMORY; - Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req)); - free(req); - return CURLE_OK; -} - -/* - * Given a full header line "name: value" (optional CRLF in the input, should - * be in the output), add to Hyper and send to the debug callback. - * - * Supports multiple headers. - */ - -CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers, - const char *line) -{ - const char *p; - const char *n; - size_t nlen; - const char *v; - size_t vlen; - bool newline = TRUE; - int numh = 0; - - if(!line) - return CURLE_OK; - n = line; - do { - size_t linelen = 0; - - p = strchr(n, ':'); - if(!p) - /* this is fine if we already added at least one header */ - return numh ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; - nlen = p - n; - p++; /* move past the colon */ - while(*p == ' ') - p++; - v = p; - p = strchr(v, '\r'); - if(!p) { - p = strchr(v, '\n'); - if(p) - linelen = 1; /* LF only */ - else { - p = strchr(v, '\0'); - newline = FALSE; /* no newline */ - } - } - else - linelen = 2; /* CRLF ending */ - linelen += (p - n); - vlen = p - v; - - if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen, - (uint8_t *)v, vlen)) { - failf(data, "hyper refused to add header '%s'", line); - return CURLE_OUT_OF_MEMORY; - } - if(data->set.verbose) { - char *ptr = NULL; - if(!newline) { - ptr = aprintf("%.*s\r\n", (int)linelen, line); - if(!ptr) - return CURLE_OUT_OF_MEMORY; - Curl_debug(data, CURLINFO_HEADER_OUT, ptr, linelen + 2); - free(ptr); - } - else - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen); - } - numh++; - n += linelen; - } while(newline); - return CURLE_OK; -} - -static CURLcode request_target(struct Curl_easy *data, - struct connectdata *conn, - const char *method, - hyper_request *req) -{ - CURLcode result; - struct dynbuf r; - - Curl_dyn_init(&r, DYN_HTTP_REQUEST); - - result = Curl_http_target(data, conn, &r); - if(result) - return result; - - if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), - Curl_dyn_len(&r))) { - failf(data, "error setting uri to hyper"); - result = CURLE_OUT_OF_MEMORY; - } - else - result = debug_request(data, method, Curl_dyn_ptr(&r)); - - Curl_dyn_free(&r); - - return result; -} - -static int uploadstreamed(void *userdata, hyper_context *ctx, - hyper_buf **chunk) -{ - size_t fillcount; - struct Curl_easy *data = (struct Curl_easy *)userdata; - CURLcode result; - char *xfer_ulbuf; - size_t xfer_ulblen; - bool eos; - int rc = HYPER_POLL_ERROR; - (void)ctx; - - result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen); - if(result) - goto out; - - result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &fillcount, &eos); - if(result) - goto out; - - if(fillcount) { - hyper_buf *copy = hyper_buf_copy((uint8_t *)xfer_ulbuf, fillcount); - if(copy) - *chunk = copy; - else { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - /* 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; - } - else { - /* paused, save a waker */ - if(data->hyp.send_body_waker) - hyper_waker_free(data->hyp.send_body_waker); - data->hyp.send_body_waker = hyper_context_waker(ctx); - 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; -} - -/* - * finalize_request() sets up last headers and optional body settings - */ -static CURLcode finalize_request(struct Curl_easy *data, - hyper_headers *headers, - hyper_request *hyperreq, - Curl_HttpReq httpreq) -{ - CURLcode result = CURLE_OK; - struct dynbuf req; - if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) { - Curl_pgrsSetUploadSize(data, 0); /* no request body */ - } - else { - hyper_body *body; - Curl_dyn_init(&req, DYN_HTTP_REQUEST); - result = Curl_http_req_complete(data, &req, httpreq); - if(result) - return result; - - /* if the "complete" above did produce more than the closing line, - parse the added headers */ - if(Curl_dyn_len(&req) != 2 || strcmp(Curl_dyn_ptr(&req), "\r\n")) { - result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req)); - if(result) - return result; - } - - Curl_dyn_free(&req); - - body = hyper_body_new(); - hyper_body_set_userdata(body, data); - hyper_body_set_data_func(body, uploadstreamed); - - if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) { - /* fail */ - result = CURLE_OUT_OF_MEMORY; - } - } - - return cr_hyper_add(data); -} - -static CURLcode cookies(struct Curl_easy *data, - struct connectdata *conn, - hyper_headers *headers) -{ - struct dynbuf req; - CURLcode result; - Curl_dyn_init(&req, DYN_HTTP_REQUEST); - - result = Curl_http_cookies(data, conn, &req); - if(!result) - result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req)); - Curl_dyn_free(&req); - return result; -} - -/* called on 1xx responses */ -static void http1xx_cb(void *arg, struct hyper_response *resp) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - hyper_headers *headers = NULL; - CURLcode result = CURLE_OK; - uint16_t http_status; - int http_version; - const uint8_t *reasonp; - size_t reason_len; - - infof(data, "Got HTTP 1xx informational"); - - http_status = hyper_response_status(resp); - http_version = hyper_response_version(resp); - reasonp = hyper_response_reason_phrase(resp); - reason_len = hyper_response_reason_phrase_len(resp); - - result = status_line(data, data->conn, - http_status, http_version, reasonp, reason_len); - if(!result) { - headers = hyper_response_headers(resp); - if(!headers) { - failf(data, "hyperstream: could not get 1xx response headers"); - result = CURLE_RECV_ERROR; - } - } - data->state.hresult = result; - - if(!result) { - /* the headers are already received */ - hyper_headers_foreach(headers, hyper_each_header, data); - /* this callback also sets data->state.hresult on error */ - - if(empty_header(data)) - result = CURLE_OUT_OF_MEMORY; - } - - if(data->state.hresult) - infof(data, "ERROR in 1xx, bail out"); -} - -/* - * Curl_http() gets called from the generic multi_do() function when an HTTP - * request is to be performed. This creates and sends a properly constructed - * HTTP request. - */ -CURLcode Curl_http(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct hyptransfer *h = &data->hyp; - hyper_io *io = NULL; - hyper_clientconn_options *options = NULL; - hyper_task *task = NULL; /* for the handshake */ - hyper_task *sendtask = NULL; /* for the send */ - hyper_clientconn *client = NULL; - hyper_request *req = NULL; - hyper_headers *headers = NULL; - hyper_task *handshake = NULL; - CURLcode result; - const char *p_accept; /* Accept: string */ - const char *method; - Curl_HttpReq httpreq; - const char *te = NULL; /* transfer-encoding */ - hyper_code rc; - - /* Always consider the DO phase done after this function call, even if there - may be parts of the request that is not yet sent, since we can deal with - the rest of the request in the PERFORM phase. */ - *done = TRUE; - result = Curl_client_start(data); - if(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) - goto out; - - infof(data, "Time for the Hyper dance"); - memset(h, 0, sizeof(struct hyptransfer)); - - result = Curl_http_host(data, conn); - if(result) - goto out; - - Curl_http_method(data, conn, &method, &httpreq); - - DEBUGASSERT(data->req.bytecount == 0); - - /* setup the authentication headers */ - { - char *pq = NULL; - if(data->state.up.query) { - pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) { - 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) - goto out; - } - - result = Curl_http_req_set_reader(data, httpreq, &te); - if(result) - goto out; - - result = Curl_http_range(data, httpreq); - if(result) - goto out; - - result = Curl_http_useragent(data); - if(result) - goto out; - - io = hyper_io_new(); - if(!io) { - failf(data, "Couldn't create hyper IO"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - /* tell Hyper how to read/write network data */ - h->io_ctx.data = data; - h->io_ctx.sockindex = FIRSTSOCKET; - hyper_io_set_userdata(io, &h->io_ctx); - hyper_io_set_read(io, Curl_hyper_recv); - hyper_io_set_write(io, Curl_hyper_send); - - /* create an executor to poll futures */ - if(!h->exec) { - h->exec = hyper_executor_new(); - if(!h->exec) { - failf(data, "Couldn't create hyper executor"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - options = hyper_clientconn_options_new(); - if(!options) { - failf(data, "Couldn't create hyper client options"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - if(conn->alpn == CURL_HTTP_VERSION_2) { - failf(data, "ALPN protocol h2 not supported with Hyper"); - result = CURLE_UNSUPPORTED_PROTOCOL; - goto out; - } - hyper_clientconn_options_set_preserve_header_case(options, 1); - hyper_clientconn_options_set_preserve_header_order(options, 1); - hyper_clientconn_options_http1_allow_multiline_headers(options, 1); - - hyper_clientconn_options_exec(options, h->exec); - - /* "Both the `io` and the `options` are consumed in this function call" */ - handshake = hyper_clientconn_handshake(io, options); - if(!handshake) { - failf(data, "Couldn't create hyper client handshake"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - io = NULL; - options = NULL; - - if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { - failf(data, "Couldn't hyper_executor_push the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - handshake = NULL; /* ownership passed on */ - - task = hyper_executor_poll(h->exec); - if(!task) { - failf(data, "Couldn't hyper_executor_poll the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - client = hyper_task_value(task); - hyper_task_free(task); - - req = hyper_request_new(); - if(!req) { - failf(data, "Couldn't hyper_request_new"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - if(!Curl_use_http_1_1plus(data, conn)) { - if(HYPERE_OK != hyper_request_set_version(req, - HYPER_HTTP_VERSION_1_0)) { - failf(data, "error setting HTTP version"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) { - failf(data, "error setting method"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - result = request_target(data, conn, method, req); - if(result) - goto out; - - headers = hyper_request_headers(req); - if(!headers) { - failf(data, "hyper_request_headers"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - rc = hyper_request_on_informational(req, http1xx_cb, data); - if(rc) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - if(data->state.aptr.host) { - result = Curl_hyper_header(data, headers, data->state.aptr.host); - if(result) - goto out; - } - -#ifndef CURL_DISABLE_PROXY - if(data->state.aptr.proxyuserpwd) { - result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd); - if(result) - goto out; - } -#endif - - if(data->state.aptr.userpwd) { - result = Curl_hyper_header(data, headers, data->state.aptr.userpwd); - if(result) - goto out; - } - - if((data->state.use_range && data->state.aptr.rangeline)) { - result = Curl_hyper_header(data, headers, data->state.aptr.rangeline); - if(result) - goto out; - } - - if(data->set.str[STRING_USERAGENT] && - *data->set.str[STRING_USERAGENT] && - data->state.aptr.uagent) { - result = Curl_hyper_header(data, headers, data->state.aptr.uagent); - if(result) - goto out; - } - - p_accept = Curl_checkheaders(data, - STRCONST("Accept")) ? NULL : "Accept: */*\r\n"; - if(p_accept) { - result = Curl_hyper_header(data, headers, p_accept); - if(result) - goto out; - } - if(te) { - result = Curl_hyper_header(data, headers, te); - if(result) - goto out; - } - -#ifndef CURL_DISABLE_ALTSVC - if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) { - char *altused = aprintf("Alt-Used: %s:%d\r\n", - conn->conn_to_host.name, conn->conn_to_port); - if(!altused) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - result = Curl_hyper_header(data, headers, altused); - if(result) - goto out; - free(altused); - } -#endif - -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy && - !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && - !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { - result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"); - if(result) - goto out; - } -#endif - - Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) { - data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); - if(!data->state.aptr.ref) - result = CURLE_OUT_OF_MEMORY; - else - result = Curl_hyper_header(data, headers, data->state.aptr.ref); - if(result) - goto out; - } - -#ifdef HAVE_LIBZ - /* we only consider transfer-encoding magic if libz support is built-in */ - result = Curl_transferencode(data); - if(result) - goto out; - result = Curl_hyper_header(data, headers, data->state.aptr.te); - if(result) - goto out; -#endif - - if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && - data->set.str[STRING_ENCODING]) { - Curl_safefree(data->state.aptr.accept_encoding); - data->state.aptr.accept_encoding = - aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); - if(!data->state.aptr.accept_encoding) - result = CURLE_OUT_OF_MEMORY; - else - result = Curl_hyper_header(data, headers, - data->state.aptr.accept_encoding); - if(result) - goto out; - } - else - Curl_safefree(data->state.aptr.accept_encoding); - - result = cookies(data, conn, headers); - if(result) - 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 out; - - result = Curl_add_custom_headers(data, FALSE, headers); - if(result) - goto out; - - result = finalize_request(data, headers, req, httpreq); - if(result) - goto out; - - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2); - - if(data->req.upload_chunky && data->req.authneg) { - data->req.upload_chunky = TRUE; - } - else { - data->req.upload_chunky = FALSE; - } - sendtask = hyper_clientconn_send(client, req); - if(!sendtask) { - failf(data, "hyper_clientconn_send"); - result = CURLE_OUT_OF_MEMORY; - goto 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 out; - } - sendtask = NULL; /* ownership passed on */ - - hyper_clientconn_free(client); - client = NULL; - - 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); - conn->datastream = Curl_hyper_stream; - - /* clear userpwd and proxyuserpwd to avoid reusing old credentials - * from reused connections */ - Curl_safefree(data->state.aptr.userpwd); -#ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuserpwd); -#endif - -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; -} - -void Curl_hyper_done(struct Curl_easy *data) -{ - struct hyptransfer *h = &data->hyp; - if(h->exec) { - hyper_executor_free(h->exec); - h->exec = NULL; - } - if(h->read_waker) { - hyper_waker_free(h->read_waker); - h->read_waker = NULL; - } - if(h->write_waker) { - hyper_waker_free(h->write_waker); - h->write_waker = NULL; - } - if(h->send_body_waker) { - hyper_waker_free(h->send_body_waker); - h->send_body_waker = NULL; - } -} - -static CURLcode cr_hyper_unpause(struct Curl_easy *data, - struct Curl_creader *reader) -{ - (void)reader; - if(data->hyp.send_body_waker) { - hyper_waker_wake(data->hyp.send_body_waker); - data->hyp.send_body_waker = NULL; - } - return CURLE_OK; -} - -/* Hyper client reader, handling unpausing */ -static const struct Curl_crtype cr_hyper_protocol = { - "cr-hyper", - Curl_creader_def_init, - Curl_creader_def_read, - Curl_creader_def_close, - Curl_creader_def_needs_rewind, - Curl_creader_def_total_length, - Curl_creader_def_resume_from, - Curl_creader_def_rewind, - cr_hyper_unpause, - Curl_creader_def_is_paused, - Curl_creader_def_done, - sizeof(struct Curl_creader) -}; - -static CURLcode cr_hyper_add(struct Curl_easy *data) -{ - struct Curl_creader *reader = NULL; - CURLcode result; - - result = Curl_creader_create(&reader, data, &cr_hyper_protocol, - CURL_CR_PROTOCOL); - if(!result) - result = Curl_creader_add(data, reader); - - if(result && reader) - Curl_creader_free(data, reader); - return result; -} - -#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */ diff --git a/lib/c-hyper.h b/lib/c-hyper.h deleted file mode 100644 index 89dd53b..0000000 --- a/lib/c-hyper.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef HEADER_CURL_HYPER_H -#define HEADER_CURL_HYPER_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) - -#include - -struct hyp_io_ctx { - struct Curl_easy *data; - int sockindex; -}; - -/* per-transfer data for the Hyper backend */ -struct hyptransfer { - hyper_waker *write_waker; - hyper_waker *read_waker; - const hyper_executor *exec; - hyper_waker *send_body_waker; - struct hyp_io_ctx io_ctx; -}; - -size_t Curl_hyper_recv(void *userp, hyper_context *ctx, - uint8_t *buf, size_t buflen); -size_t Curl_hyper_send(void *userp, hyper_context *ctx, - const uint8_t *buf, size_t buflen); -CURLcode Curl_hyper_stream(struct Curl_easy *data, - struct connectdata *conn, - int *didwhat, - int select_res); - -CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers, - const char *line); -void Curl_hyper_done(struct Curl_easy *); - -#else -#define Curl_hyper_done(x) - -#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */ -#endif /* HEADER_CURL_HYPER_H */ diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 6b7f983..2e13f39 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -27,9 +27,6 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) #include -#ifdef USE_HYPER -#include -#endif #include "urldata.h" #include "dynbuf.h" #include "sendf.h" @@ -184,9 +181,6 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, make sure that it is not accidentally used for the document request after we have connected. So let's free and clear it here. */ Curl_safefree(data->state.aptr.proxyuserpwd); -#ifdef USE_HYPER - data->state.hconnect = FALSE; -#endif break; } } @@ -209,10 +203,9 @@ static void tunnel_free(struct Curl_cfilter *cf, static bool tunnel_want_send(struct h1_tunnel_state *ts) { - return (ts->tunnel_state == H1_TUNNEL_CONNECT); + return ts->tunnel_state == H1_TUNNEL_CONNECT; } -#ifndef USE_HYPER static CURLcode start_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts) @@ -529,337 +522,6 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, return result; } -#else /* USE_HYPER */ - -static CURLcode CONNECT_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - char **pauthority, - char **phost_header) -{ - const char *hostname; - int port; - bool ipv6_ip; - CURLcode result; - char *authority; /* for CONNECT, the destination host + port */ - char *host_header = NULL; /* Host: authority */ - - result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - if(result) - return result; - - authority = aprintf("%s%s%s:%d", ipv6_ip ? "[":"", hostname, - ipv6_ip ? "]" : "", port); - if(!authority) - return CURLE_OUT_OF_MEMORY; - - /* If user is not overriding the Host header later */ - if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { - host_header = aprintf("Host: %s\r\n", authority); - if(!host_header) { - free(authority); - return CURLE_OUT_OF_MEMORY; - } - } - *pauthority = authority; - *phost_header = host_header; - return CURLE_OK; -} - -/* The Hyper version of CONNECT */ -static CURLcode start_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts) -{ - struct connectdata *conn = cf->conn; - struct hyptransfer *h = &data->hyp; - curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); - hyper_io *io = NULL; - hyper_request *req = NULL; - hyper_headers *headers = NULL; - hyper_clientconn_options *options = NULL; - hyper_task *handshake = NULL; - hyper_task *task = NULL; /* for the handshake */ - hyper_clientconn *client = NULL; - hyper_task *sendtask = NULL; /* for the send */ - char *authority = NULL; /* for CONNECT */ - char *host_header = NULL; /* Host: */ - CURLcode result = CURLE_OUT_OF_MEMORY; - (void)ts; - - io = hyper_io_new(); - if(!io) { - failf(data, "Couldn't create hyper IO"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - /* tell Hyper how to read/write network data */ - h->io_ctx.data = data; - h->io_ctx.sockindex = cf->sockindex; - hyper_io_set_userdata(io, &h->io_ctx); - hyper_io_set_read(io, Curl_hyper_recv); - hyper_io_set_write(io, Curl_hyper_send); - conn->sockfd = tunnelsocket; - - data->state.hconnect = TRUE; - - /* create an executor to poll futures */ - if(!h->exec) { - h->exec = hyper_executor_new(); - if(!h->exec) { - failf(data, "Couldn't create hyper executor"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - - options = hyper_clientconn_options_new(); - if(!options) { - failf(data, "Couldn't create hyper client options"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - hyper_clientconn_options_set_preserve_header_case(options, 1); - hyper_clientconn_options_set_preserve_header_order(options, 1); - - hyper_clientconn_options_exec(options, h->exec); - - /* "Both the `io` and the `options` are consumed in this function - call" */ - handshake = hyper_clientconn_handshake(io, options); - if(!handshake) { - failf(data, "Couldn't create hyper client handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - io = NULL; - options = NULL; - - if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { - failf(data, "Couldn't hyper_executor_push the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - handshake = NULL; /* ownership passed on */ - - task = hyper_executor_poll(h->exec); - if(!task) { - failf(data, "Couldn't hyper_executor_poll the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - client = hyper_task_value(task); - hyper_task_free(task); - - req = hyper_request_new(); - if(!req) { - failf(data, "Couldn't hyper_request_new"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(hyper_request_set_method(req, (uint8_t *)"CONNECT", - strlen("CONNECT"))) { - failf(data, "error setting method"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - /* This only happens if we have looped here due to authentication - reasons, and we do not really use the newly cloned URL here - then. Just free() it. */ - Curl_safefree(data->req.newurl); - - result = CONNECT_host(cf, data, &authority, &host_header); - if(result) - goto error; - - infof(data, "Establish HTTP proxy tunnel to %s", authority); - - if(hyper_request_set_uri(req, (uint8_t *)authority, - strlen(authority))) { - failf(data, "error setting path"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(data->set.verbose) { - char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority); - if(!se) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); - free(se); - } - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, - authority, TRUE); - if(result) - goto error; - Curl_safefree(authority); - - /* default is 1.1 */ - if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && - (HYPERE_OK != hyper_request_set_version(req, - HYPER_HTTP_VERSION_1_0))) { - failf(data, "error setting HTTP version"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - headers = hyper_request_headers(req); - if(!headers) { - failf(data, "hyper_request_headers"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(host_header) { - result = Curl_hyper_header(data, headers, host_header); - if(result) - goto error; - Curl_safefree(host_header); - } - - if(data->state.aptr.proxyuserpwd) { - result = Curl_hyper_header(data, headers, - data->state.aptr.proxyuserpwd); - if(result) - goto error; - } - - if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { - struct dynbuf ua; - Curl_dyn_init(&ua, DYN_HTTP_REQUEST); - result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", - data->set.str[STRING_USERAGENT]); - if(result) - goto error; - result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); - if(result) - goto error; - Curl_dyn_free(&ua); - } - - if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { - result = Curl_hyper_header(data, headers, - "Proxy-Connection: Keep-Alive"); - if(result) - goto error; - } - - result = Curl_add_custom_headers(data, TRUE, headers); - if(result) - goto error; - - result = Curl_creader_set_null(data); - if(result) - goto error; - - sendtask = hyper_clientconn_send(client, req); - if(!sendtask) { - failf(data, "hyper_clientconn_send"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - 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; - } - sendtask = NULL; /* ownership passed on */ - - hyper_clientconn_free(client); - client = NULL; - -error: - free(host_header); - free(authority); - 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; -} - -static CURLcode send_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts, - bool *done) -{ - struct hyptransfer *h = &data->hyp; - struct connectdata *conn = cf->conn; - hyper_task *task = NULL; - hyper_error *hypererr = NULL; - CURLcode result = CURLE_OK; - - (void)ts; - (void)conn; - do { - task = hyper_executor_poll(h->exec); - if(task) { - bool error = hyper_task_type(task) == HYPER_TASK_ERROR; - if(error) - hypererr = hyper_task_value(task); - hyper_task_free(task); - if(error) { - /* this could probably use a better error code? */ - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - } while(task); -error: - *done = (result == CURLE_OK); - if(hypererr) { - uint8_t errbuf[256]; - size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); - failf(data, "Hyper: %.*s", (int)errlen, errbuf); - hyper_error_free(hypererr); - } - return result; -} - -static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts, - bool *done) -{ - struct hyptransfer *h = &data->hyp; - CURLcode result; - int didwhat; - - (void)ts; - result = Curl_hyper_stream(data, cf->conn, &didwhat, - CURL_CSELECT_IN | CURL_CSELECT_OUT); - *done = data->req.done; - if(result || !*done) - return result; - if(h->exec) { - hyper_executor_free(h->exec); - h->exec = NULL; - } - if(h->read_waker) { - hyper_waker_free(h->read_waker); - h->read_waker = NULL; - } - if(h->write_waker) { - hyper_waker_free(h->write_waker); - h->write_waker = NULL; - } - return result; -} - -#endif /* USE_HYPER */ - static CURLcode H1_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts) diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index e687dd1..771ecc1 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -865,7 +865,9 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, if(nwritten < 0) { if(result != CURLE_AGAIN) return NGHTTP2_ERR_CALLBACK_FAILURE; +#ifdef DEBUGBUILD nwritten = 0; +#endif } DEBUGASSERT((size_t)nwritten == len); return 0; diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index dd7cdcb..2b39e98 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -24,13 +24,14 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) +#if !defined(CURL_DISABLE_HTTP) #include "urldata.h" #include #include "curl_trc.h" #include "cfilters.h" #include "connect.h" +#include "hostip.h" #include "multiif.h" #include "cf-https-connect.h" #include "http2.h" @@ -42,6 +43,10 @@ #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + typedef enum { CF_HC_INIT, CF_HC_CONNECT, @@ -55,7 +60,7 @@ struct cf_hc_baller { CURLcode result; struct curltime started; int reply_ms; - BIT(enabled); + enum alpnid alpn_id; BIT(shutdown); }; @@ -73,7 +78,7 @@ static void cf_hc_baller_reset(struct cf_hc_baller *b, static bool cf_hc_baller_is_active(struct cf_hc_baller *b) { - return b->enabled && b->cf && !b->result; + return b->cf && !b->result; } static bool cf_hc_baller_has_started(struct cf_hc_baller *b) @@ -84,7 +89,7 @@ static bool cf_hc_baller_has_started(struct cf_hc_baller *b) static int cf_hc_baller_reply_ms(struct cf_hc_baller *b, struct Curl_easy *data) { - if(b->reply_ms < 0) + if(b->cf && (b->reply_ms < 0)) b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS, &b->reply_ms, NULL); return b->reply_ms; @@ -116,26 +121,53 @@ struct cf_hc_ctx { const struct Curl_dns_entry *remotehost; struct curltime started; /* when connect started */ CURLcode result; /* overall result */ - struct cf_hc_baller h3_baller; - struct cf_hc_baller h21_baller; + struct cf_hc_baller ballers[2]; + size_t baller_count; unsigned int soft_eyeballs_timeout_ms; unsigned int hard_eyeballs_timeout_ms; }; +static void cf_hc_baller_assign(struct cf_hc_baller *b, + enum alpnid alpn_id) +{ + b->alpn_id = alpn_id; + switch(b->alpn_id) { + case ALPN_h3: + b->name = "h3"; + break; + case ALPN_h2: + b->name = "h2"; + break; + case ALPN_h1: + b->name = "h1"; + break; + default: + b->result = CURLE_FAILED_INIT; + break; + } +} + static void cf_hc_baller_init(struct cf_hc_baller *b, struct Curl_cfilter *cf, struct Curl_easy *data, - const char *name, int transport) { struct cf_hc_ctx *ctx = cf->ctx; struct Curl_cfilter *save = cf->next; - b->name = name; cf->next = NULL; b->started = Curl_now(); - b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost, - transport, CURL_CF_SSL_ENABLE); + switch(b->alpn_id) { + case ALPN_h3: + transport = TRNSPRT_QUIC; + break; + default: + break; + } + + if(!b->result) + b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost, + transport, CURL_CF_SSL_ENABLE); b->cf = cf->next; cf->next = save; } @@ -157,10 +189,11 @@ static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b, static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; + size_t i; if(ctx) { - cf_hc_baller_reset(&ctx->h3_baller, data); - cf_hc_baller_reset(&ctx->h21_baller, data); + for(i = 0; i < ctx->baller_count; ++i) + cf_hc_baller_reset(&ctx->ballers[i], data); ctx->state = CF_HC_INIT; ctx->result = CURLE_OK; ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; @@ -175,12 +208,12 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, struct cf_hc_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; int reply_ms; + size_t i; DEBUGASSERT(winner->cf); - if(winner != &ctx->h3_baller) - cf_hc_baller_reset(&ctx->h3_baller, data); - if(winner != &ctx->h21_baller) - cf_hc_baller_reset(&ctx->h21_baller, data); + for(i = 0; i < ctx->baller_count; ++i) + if(winner != &ctx->ballers[i]) + cf_hc_baller_reset(&ctx->ballers[i], data); reply_ms = cf_hc_baller_reply_ms(winner, data); if(reply_ms >= 0) @@ -218,31 +251,40 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, } -static bool time_to_start_h21(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct curltime now) +static bool time_to_start_next(struct Curl_cfilter *cf, + struct Curl_easy *data, + size_t idx, struct curltime now) { struct cf_hc_ctx *ctx = cf->ctx; timediff_t elapsed_ms; + size_t i; - if(!ctx->h21_baller.enabled || cf_hc_baller_has_started(&ctx->h21_baller)) + if(idx >= ctx->baller_count) return FALSE; - - if(!ctx->h3_baller.enabled || !cf_hc_baller_is_active(&ctx->h3_baller)) + if(cf_hc_baller_has_started(&ctx->ballers[idx])) + return FALSE; + for(i = 0; i < idx; i++) { + if(!ctx->ballers[i].result) + break; + } + if(i == idx) { + CURL_TRC_CF(data, cf, "all previous ballers have failed, time to start " + "baller %zu [%s]", idx, ctx->ballers[idx].name); return TRUE; - + } elapsed_ms = Curl_timediff(now, ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting h21", - ctx->hard_eyeballs_timeout_ms); + CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting %s", + ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name); return TRUE; } - if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { - if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) { - CURL_TRC_CF(data, cf, "soft timeout of %dms reached, h3 has not " - "seen any data, starting h21", - ctx->soft_eyeballs_timeout_ms); + if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) { + if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) { + CURL_TRC_CF(data, cf, "soft timeout of %dms reached, %s has not " + "seen any data, starting %s", + ctx->soft_eyeballs_timeout_ms, + ctx->ballers[idx - 1].name, ctx->ballers[idx].name); return TRUE; } /* set the effective hard timeout again */ @@ -259,6 +301,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, struct cf_hc_ctx *ctx = cf->ctx; struct curltime now; CURLcode result = CURLE_OK; + size_t i, failed_ballers; (void)blocking; if(cf->connected) { @@ -270,51 +313,57 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, now = Curl_now(); switch(ctx->state) { case CF_HC_INIT: - DEBUGASSERT(!ctx->h3_baller.cf); - DEBUGASSERT(!ctx->h21_baller.cf); DEBUGASSERT(!cf->next); + for(i = 0; i < ctx->baller_count; i++) + DEBUGASSERT(!ctx->ballers[i].cf); CURL_TRC_CF(data, cf, "connect, init"); ctx->started = now; - if(ctx->h3_baller.enabled) { - cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC); - if(ctx->h21_baller.enabled) - Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); + cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport); + if(ctx->baller_count > 1) { + Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); + CURL_TRC_CF(data, cf, "set expire for starting next baller in %ums", + ctx->soft_eyeballs_timeout_ms); } - else if(ctx->h21_baller.enabled) - cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", - cf->conn->transport); ctx->state = CF_HC_CONNECT; FALLTHROUGH(); case CF_HC_CONNECT: - if(cf_hc_baller_is_active(&ctx->h3_baller)) { - result = cf_hc_baller_connect(&ctx->h3_baller, cf, data, done); + if(cf_hc_baller_is_active(&ctx->ballers[0])) { + result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done); if(!result && *done) { - result = baller_connected(cf, data, &ctx->h3_baller); + result = baller_connected(cf, data, &ctx->ballers[0]); goto out; } } - if(time_to_start_h21(cf, data, now)) { - cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", - cf->conn->transport); + if(time_to_start_next(cf, data, 1, now)) { + cf_hc_baller_init(&ctx->ballers[1], cf, data, cf->conn->transport); } - if(cf_hc_baller_is_active(&ctx->h21_baller)) { - CURL_TRC_CF(data, cf, "connect, check h21"); - result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done); + if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) { + CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name); + result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done); if(!result && *done) { - result = baller_connected(cf, data, &ctx->h21_baller); + result = baller_connected(cf, data, &ctx->ballers[1]); goto out; } } - if((!ctx->h3_baller.enabled || ctx->h3_baller.result) && - (!ctx->h21_baller.enabled || ctx->h21_baller.result)) { - /* both failed or disabled. we give up */ + failed_ballers = 0; + for(i = 0; i < ctx->baller_count; i++) { + if(ctx->ballers[i].result) + ++failed_ballers; + } + + if(failed_ballers == ctx->baller_count) { + /* all have failed. we give up */ CURL_TRC_CF(data, cf, "connect, all failed"); - result = ctx->result = ctx->h3_baller.enabled ? - ctx->h3_baller.result : ctx->h21_baller.result; + for(i = 0; i < ctx->baller_count; i++) { + if(ctx->ballers[i].result) { + result = ctx->ballers[i].result; + break; + } + } ctx->state = CF_HC_FAILURE; goto out; } @@ -344,7 +393,6 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct cf_hc_ctx *ctx = cf->ctx; - struct cf_hc_baller *ballers[2]; size_t i; CURLcode result = CURLE_OK; @@ -356,10 +404,8 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, /* shutdown all ballers that have not done so already. If one fails, * continue shutting down others until all are shutdown. */ - ballers[0] = &ctx->h3_baller; - ballers[1] = &ctx->h21_baller; - for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { - struct cf_hc_baller *b = ballers[i]; + for(i = 0; i < ctx->baller_count; i++) { + struct cf_hc_baller *b = &ctx->ballers[i]; bool bdone = FALSE; if(!cf_hc_baller_is_active(b) || b->shutdown) continue; @@ -369,14 +415,14 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, } *done = TRUE; - for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { - if(ballers[i] && !ballers[i]->shutdown) + for(i = 0; i < ctx->baller_count; i++) { + if(!ctx->ballers[i].shutdown) *done = FALSE; } if(*done) { - for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { - if(ballers[i] && ballers[i]->result) - result = ballers[i]->result; + for(i = 0; i < ctx->baller_count; i++) { + if(ctx->ballers[i].result) + result = ctx->ballers[i].result; } } CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); @@ -389,13 +435,10 @@ static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, { if(!cf->connected) { struct cf_hc_ctx *ctx = cf->ctx; - struct cf_hc_baller *ballers[2]; size_t i; - ballers[0] = &ctx->h3_baller; - ballers[1] = &ctx->h21_baller; - for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { - struct cf_hc_baller *b = ballers[i]; + for(i = 0; i < ctx->baller_count; i++) { + struct cf_hc_baller *b = &ctx->ballers[i]; if(!cf_hc_baller_is_active(b)) continue; Curl_conn_cf_adjust_pollset(b->cf, data, ps); @@ -408,13 +451,16 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; + size_t i; if(cf->connected) return cf->next->cft->has_data_pending(cf->next, data); CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending"); - return cf_hc_baller_data_pending(&ctx->h3_baller, data) - || cf_hc_baller_data_pending(&ctx->h21_baller, data); + for(i = 0; i < ctx->baller_count; i++) + if(cf_hc_baller_data_pending(&ctx->ballers[i], data)) + return TRUE; + return FALSE; } static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf, @@ -422,21 +468,17 @@ static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf, int query) { struct cf_hc_ctx *ctx = cf->ctx; - struct Curl_cfilter *cfb; struct curltime t, tmax; + size_t i; memset(&tmax, 0, sizeof(tmax)); - memset(&t, 0, sizeof(t)); - cfb = ctx->h21_baller.enabled ? ctx->h21_baller.cf : NULL; - if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) - tmax = t; - } - memset(&t, 0, sizeof(t)); - cfb = ctx->h3_baller.enabled ? ctx->h3_baller.cf : NULL; - if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) - tmax = t; + for(i = 0; i < ctx->baller_count; i++) { + struct Curl_cfilter *cfb = ctx->ballers[i].cf; + memset(&t, 0, sizeof(t)); + if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { + if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) + tmax = t; + } } return tmax; } @@ -446,6 +488,7 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, int query, int *pres1, void *pres2) { struct cf_hc_ctx *ctx = cf->ctx; + size_t i; if(!cf->connected) { switch(query) { @@ -460,11 +503,11 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, 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; - } + for(i = 0; i < ctx->baller_count; i++) + if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) { + *pres1 = TRUE; + return CURLE_OK; + } break; } default: @@ -482,14 +525,17 @@ static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf, { struct cf_hc_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; + size_t i; 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; + for(i = 0; i < ctx->baller_count; i++) { + result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2); + if(result && (result != CURLE_AGAIN)) + goto out; + } + result = CURLE_OK; } +out: return result; } @@ -537,23 +583,37 @@ struct Curl_cftype Curl_cft_http_connect = { static CURLcode cf_hc_create(struct Curl_cfilter **pcf, struct Curl_easy *data, const struct Curl_dns_entry *remotehost, - bool try_h3, bool try_h21) + enum alpnid *alpnids, size_t alpn_count) { struct Curl_cfilter *cf = NULL; struct cf_hc_ctx *ctx; CURLcode result = CURLE_OK; + size_t i; + + DEBUGASSERT(alpnids); + DEBUGASSERT(alpn_count); + DEBUGASSERT(alpn_count <= ARRAYSIZE(ctx->ballers)); + if(!alpn_count || (alpn_count > ARRAYSIZE(ctx->ballers))) { + failf(data, "https-connect filter create with unsupported %zu ALPN ids", + alpn_count); + return CURLE_FAILED_INIT; + } - (void)data; ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } ctx->remotehost = remotehost; - ctx->h3_baller.enabled = try_h3; - ctx->h21_baller.enabled = try_h21; + for(i = 0; i < alpn_count; ++i) + cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]); + for(; i < ARRAYSIZE(ctx->ballers); ++i) + ctx->ballers[i].alpn_id = ALPN_none; + ctx->baller_count = alpn_count; result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx); + CURL_TRC_CF(data, cf, "created with %zu ALPNs -> %d", + ctx->baller_count, result); if(result) goto out; ctx = NULL; @@ -569,13 +629,13 @@ static CURLcode cf_http_connect_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, const struct Curl_dns_entry *remotehost, - bool try_h3, bool try_h21) + enum alpnid *alpn_ids, size_t alpn_count) { struct Curl_cfilter *cf; CURLcode result = CURLE_OK; DEBUGASSERT(data); - result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21); + result = cf_hc_create(&cf, data, remotehost, alpn_ids, alpn_count); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -588,33 +648,88 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, int sockindex, const struct Curl_dns_entry *remotehost) { - bool try_h3 = FALSE, try_h21 = TRUE; /* defaults, for now */ + enum alpnid alpn_ids[2]; + size_t alpn_count = 0; CURLcode result = CURLE_OK; (void)sockindex; (void)remotehost; - if(!conn->bits.tls_enable_alpn) - goto out; - - if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { - result = Curl_conn_may_http3(data, conn); - if(result) /* cannot do it */ - goto out; - try_h3 = TRUE; - try_h21 = FALSE; + if(conn->bits.tls_enable_alpn) { + switch(data->state.httpwant) { + case CURL_HTTP_VERSION_NONE: + /* No preferences by transfer setup. Choose best defaults */ +#ifdef USE_HTTPSRR + if(conn->dns_entry && conn->dns_entry->hinfo && + !conn->dns_entry->hinfo->no_def_alpn) { + size_t i, j; + for(i = 0; i < ARRAYSIZE(conn->dns_entry->hinfo->alpns) && + alpn_count < ARRAYSIZE(alpn_ids); ++i) { + bool present = FALSE; + enum alpnid alpn = conn->dns_entry->hinfo->alpns[i]; + for(j = 0; j < alpn_count; ++j) { + if(alpn == alpn_ids[j]) { + present = TRUE; + break; + } + } + if(!present) { + switch(alpn) { + case ALPN_h3: + if(Curl_conn_may_http3(data, conn)) + break; /* not possible */ + FALLTHROUGH(); + case ALPN_h2: + case ALPN_h1: + alpn_ids[alpn_count++] = alpn; + break; + default: /* ignore */ + break; + } + } + } + } +#endif + if(!alpn_count) + alpn_ids[alpn_count++] = ALPN_h2; + break; + case CURL_HTTP_VERSION_3ONLY: + result = Curl_conn_may_http3(data, conn); + if(result) /* cannot do it */ + goto out; + alpn_ids[alpn_count++] = ALPN_h3; + break; + case CURL_HTTP_VERSION_3: + /* We assume that silently not even trying H3 is ok here */ + /* TODO: should we fail instead? */ + if(Curl_conn_may_http3(data, conn) == CURLE_OK) + alpn_ids[alpn_count++] = ALPN_h3; + alpn_ids[alpn_count++] = ALPN_h2; + break; + case CURL_HTTP_VERSION_2_0: + case CURL_HTTP_VERSION_2TLS: + case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE: + alpn_ids[alpn_count++] = ALPN_h2; + break; + case CURL_HTTP_VERSION_1_0: + case CURL_HTTP_VERSION_1_1: + alpn_ids[alpn_count++] = ALPN_h1; + break; + default: + alpn_ids[alpn_count++] = ALPN_h2; + break; + } } - else if(data->state.httpwant >= CURL_HTTP_VERSION_3) { - /* We assume that silently not even trying H3 is ok here */ - /* TODO: should we fail instead? */ - try_h3 = (Curl_conn_may_http3(data, conn) == CURLE_OK); - try_h21 = TRUE; + + /* If we identified ALPNs to use, install our filter. Otherwise, + * install nothing, so our call will use a default connect setup. */ + if(alpn_count) { + result = cf_http_connect_add(data, conn, sockindex, remotehost, + alpn_ids, alpn_count); } - result = cf_http_connect_add(data, conn, sockindex, remotehost, - try_h3, try_h21); out: return result; } -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ +#endif /* !defined(CURL_DISABLE_HTTP) */ diff --git a/lib/cf-https-connect.h b/lib/cf-https-connect.h index 6a39527..4ff9ef8 100644 --- a/lib/cf-https-connect.h +++ b/lib/cf-https-connect.h @@ -25,7 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) +#if !defined(CURL_DISABLE_HTTP) struct Curl_cfilter; struct Curl_easy; @@ -54,5 +54,5 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, const struct Curl_dns_entry *remotehost); -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ +#endif /* !defined(CURL_DISABLE_HTTP) */ #endif /* HEADER_CURL_CF_HTTP_H */ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 497a3b9..edda348 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -306,9 +306,9 @@ tcpkeepalive(struct Curl_easy *data, * Assign the address `ai` to the Curl_sockaddr_ex `dest` and * set the transport used. */ -void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, - const struct Curl_addrinfo *ai, - int transport) +CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, + const struct Curl_addrinfo *ai, + int transport) { /* * The Curl_sockaddr_ex structure is basically libcurl's external API @@ -334,9 +334,13 @@ void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, } dest->addrlen = (unsigned int)ai->ai_addrlen; - if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) - dest->addrlen = sizeof(struct Curl_sockaddr_storage); + if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) { + DEBUGASSERT(0); + return CURLE_TOO_LARGE; + } + memcpy(&dest->curl_sa_addr, ai->ai_addr, dest->addrlen); + return CURLE_OK; } static CURLcode socket_open(struct Curl_easy *data, @@ -395,12 +399,16 @@ CURLcode Curl_socket_open(struct Curl_easy *data, curl_socket_t *sockfd) { struct Curl_sockaddr_ex dummy; + CURLcode result; if(!addr) /* if the caller does not want info back, use a local temp copy */ addr = &dummy; - Curl_sock_assign_addr(addr, ai, transport); + result = Curl_sock_assign_addr(addr, ai, transport); + if(result) + return result; + return socket_open(data, addr, sockfd); } @@ -959,14 +967,20 @@ struct cf_socket_ctx { BIT(active); }; -static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, - const struct Curl_addrinfo *ai, - int transport) +static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, + const struct Curl_addrinfo *ai, + int transport) { + CURLcode result; + memset(ctx, 0, sizeof(*ctx)); ctx->sock = CURL_SOCKET_BAD; ctx->transport = transport; - Curl_sock_assign_addr(&ctx->addr, ai, transport); + + result = Curl_sock_assign_addr(&ctx->addr, ai, transport); + if(result) + return result; + #ifdef DEBUGBUILD { char *p = getenv("CURL_DBG_SOCK_WBLOCK"); @@ -995,6 +1009,8 @@ static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, } } #endif + + return result; } static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -1282,7 +1298,7 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data, rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen); #elif defined(MSG_FASTOPEN) /* old Linux */ - if(cf->conn->given->flags & PROTOPT_SSL) + if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen); else rc = 0; /* Do nothing */ @@ -1443,7 +1459,7 @@ static bool cf_socket_data_pending(struct Curl_cfilter *cf, (void)data; readable = SOCKET_READABLE(ctx->sock, 0); - return (readable > 0 && (readable & CURL_CSELECT_IN)); + return readable > 0 && (readable & CURL_CSELECT_IN); } #ifdef USE_WINSOCK @@ -1805,7 +1821,10 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_socket_ctx_init(ctx, ai, transport); + + result = cf_socket_ctx_init(ctx, ai, transport); + if(result) + goto out; result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx); @@ -1831,7 +1850,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, /* QUIC needs a connected socket, nonblocking */ DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD); - rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, + rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, /* NOLINT FIXME */ (curl_socklen_t)ctx->addr.addrlen); if(-1 == rc) { return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO); @@ -1954,7 +1973,10 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_socket_ctx_init(ctx, ai, transport); + + result = cf_socket_ctx_init(ctx, ai, transport); + if(result) + goto out; result = Curl_cf_create(&cf, &Curl_cft_udp, ctx); @@ -2006,7 +2028,10 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_socket_ctx_init(ctx, ai, transport); + + result = cf_socket_ctx_init(ctx, ai, transport); + if(result) + goto out; result = Curl_cf_create(&cf, &Curl_cft_unix, ctx); diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 6510349..1ce8404 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -95,9 +95,9 @@ void Curl_sndbuf_init(curl_socket_t sockfd); * Assign the address `ai` to the Curl_sockaddr_ex `dest` and * set the transport used. */ -void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, - const struct Curl_addrinfo *ai, - int transport); +CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, + const struct Curl_addrinfo *ai, + int transport); /** * Creates a cfilter that opens a TCP socket to the given address diff --git a/lib/cfilters.c b/lib/cfilters.c index ca1d7b3..3be0840 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -494,13 +494,35 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) for(; cf; cf = cf->next) { if(cf->cft->flags & CF_TYPE_MULTIPLEX) return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT - || cf->cft->flags & CF_TYPE_SSL) + if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL)) return FALSE; } return FALSE; } +unsigned char Curl_conn_http_version(struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_UNKNOWN_OPTION; + unsigned char v = 0; + + cf = data->conn ? data->conn->cfilter[FIRSTSOCKET] : NULL; + for(; cf; cf = cf->next) { + if(cf->cft->flags & CF_TYPE_HTTP) { + int value = 0; + result = cf->cft->query(cf, data, CF_QUERY_HTTP_VERSION, &value, NULL); + if(!result && ((value < 0) || (value > 255))) + result = CURLE_FAILED_INIT; + else + v = (unsigned char)value; + break; + } + if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL)) + break; + } + return (unsigned char)(result ? 0 : v); +} + bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf; @@ -711,18 +733,6 @@ static CURLcode cf_cntrl_all(struct connectdata *conn, return result; } -void Curl_conn_ev_data_attach(struct connectdata *conn, - struct Curl_easy *data) -{ - cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL); -} - -void Curl_conn_ev_data_detach(struct connectdata *conn, - struct Curl_easy *data) -{ - cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL); -} - CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data) { return cf_cntrl_all(data->conn, data, FALSE, diff --git a/lib/cfilters.h b/lib/cfilters.h index af696f5..2d5599a 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -132,8 +132,6 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, * to all filters in the chain. Overall result is always CURLE_OK. */ /* data event arg1 arg2 return */ -#define CF_CTRL_DATA_ATTACH 1 /* 0 NULL ignored */ -#define CF_CTRL_DATA_DETACH 2 /* 0 NULL ignored */ #define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */ #define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */ #define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */ @@ -178,6 +176,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, #define CF_QUERY_STREAM_ERROR 6 /* error code - */ #define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */ #define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */ +#define CF_QUERY_HTTP_VERSION 9 /* number (10/11/20/30) - */ /** * Query the cfilter for properties. Filters ignorant of a query will @@ -197,11 +196,13 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, * CF_TYPE_SSL: provide SSL/TLS * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles * CF_TYPE_PROXY provides proxying + * CF_TYPE_HTTP implement a version of the HTTP protocol */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) #define CF_TYPE_MULTIPLEX (1 << 2) #define CF_TYPE_PROXY (1 << 3) +#define CF_TYPE_HTTP (1 << 4) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { @@ -395,6 +396,12 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); /** + * Return the HTTP version used on the FIRSTSOCKET connection filters + * or 0 if unknown. Value otherwise is 09, 10, 11, etc. + */ +unsigned char Curl_conn_http_version(struct Curl_easy *data); + +/** * Close the filter chain at `sockindex` for connection `data->conn`. * Filters remain in place and may be connected again afterwards. */ @@ -477,24 +484,6 @@ ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex, const void *buf, size_t len, bool eos, CURLcode *code); /** - * The easy handle `data` is being attached to `conn`. This does - * not mean that data will actually do a transfer. Attachment is - * also used for temporary actions on the connection. - */ -void Curl_conn_ev_data_attach(struct connectdata *conn, - struct Curl_easy *data); - -/** - * The easy handle `data` is being detached (no longer served) - * by connection `conn`. All filters are informed to release any resources - * related to `data`. - * Note: there may be several `data` attached to a connection at the same - * time. - */ -void Curl_conn_ev_data_detach(struct connectdata *conn, - struct Curl_easy *data); - -/** * Notify connection filters that they need to setup data for * a transfer. */ diff --git a/lib/conncache.c b/lib/conncache.c index 589205b..85435bf 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -100,6 +100,8 @@ 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 size_t cpool_shutdown_dest_count(struct cpool *cpool, + const char *destination); static struct cpool_bundle *cpool_bundle_create(const char *dest, size_t dest_len) @@ -285,6 +287,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, struct cpool_bundle *bundle; size_t dest_limit = 0; size_t total_limit = 0; + size_t shutdowns; int result = CPOOL_LIMIT_OK; if(!cpool) @@ -300,8 +303,12 @@ int Curl_cpool_check_limits(struct Curl_easy *data, CPOOL_LOCK(cpool); if(dest_limit) { + size_t live; + bundle = cpool_find_bundle(cpool, conn); - while(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) { + live = bundle ? Curl_llist_count(&bundle->conns) : 0; + shutdowns = cpool_shutdown_dest_count(cpool, conn->destination); + while(!shutdowns && bundle && live >= dest_limit) { struct connectdata *oldest_idle = NULL; /* The bundle is full. Extract the oldest connection that may * be removed now, if there is one. */ @@ -317,15 +324,18 @@ int Curl_cpool_check_limits(struct Curl_easy *data, /* in case the bundle was destroyed in disconnect, look it up again */ bundle = cpool_find_bundle(cpool, conn); + live = bundle ? Curl_llist_count(&bundle->conns) : 0; + shutdowns = cpool_shutdown_dest_count(cpool, conn->destination); } - if(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) { + if((live + shutdowns) >= dest_limit) { result = CPOOL_LIMIT_DEST; goto out; } } if(total_limit) { - while(cpool->num_conn >= total_limit) { + shutdowns = Curl_llist_count(&cpool->shutdowns); + while((cpool->num_conn + shutdowns) >= total_limit) { struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool); if(!oldest_idle) break; @@ -335,8 +345,9 @@ int Curl_cpool_check_limits(struct Curl_easy *data, "limit of %zu", oldest_idle->connection_id, cpool->num_conn, total_limit)); Curl_cpool_disconnect(data, oldest_idle, FALSE); + shutdowns = Curl_llist_count(&cpool->shutdowns); } - if(cpool->num_conn >= total_limit) { + if((cpool->num_conn + shutdowns) >= total_limit) { result = CPOOL_LIMIT_TOTAL; goto out; } @@ -374,7 +385,8 @@ CURLcode Curl_cpool_add_conn(struct Curl_easy *data, cpool->num_conn++; DEBUGF(infof(data, "Added connection %" FMT_OFF_T ". " "The cache now contains %zu members", - conn->connection_id, cpool->num_conn)); + conn->connection_id, + cpool->num_conn + Curl_llist_count(&cpool->shutdowns))); out: CPOOL_UNLOCK(cpool); @@ -612,6 +624,21 @@ bool Curl_cpool_find(struct Curl_easy *data, return result; } +/* How many connections to the given destination are in shutdown? */ +static size_t cpool_shutdown_dest_count(struct cpool *cpool, + const char *destination) +{ + size_t n = 0; + struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns); + while(e) { + struct connectdata *conn = Curl_node_elem(e); + if(!strcmp(destination, conn->destination)) + ++n; + e = Curl_node_next(e); + } + return n; +} + static void cpool_shutdown_discard_all(struct cpool *cpool) { struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns); @@ -742,12 +769,12 @@ static void cpool_discard_conn(struct cpool *cpool, /* Add the connection to our shutdown list for non-blocking shutdown * during multi processing. */ - if(data->multi && data->multi->max_shutdown_connections > 0 && - (data->multi->max_shutdown_connections >= - (long)Curl_llist_count(&cpool->shutdowns))) { + if(data->multi && data->multi->max_total_connections > 0 && + (data->multi->max_total_connections <= + (long)(cpool->num_conn + Curl_llist_count(&cpool->shutdowns)))) { DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection " - "due to limit of %ld", - data->multi->max_shutdown_connections)); + "due to connection limit of %ld", + data->multi->max_total_connections)); cpool_shutdown_destroy_oldest(cpool); } @@ -767,8 +794,8 @@ static void cpool_discard_conn(struct cpool *cpool, 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))); + " to shutdowns, now %zu conns in shutdown", + conn->connection_id, Curl_llist_count(&cpool->shutdowns))); } void Curl_cpool_disconnect(struct Curl_easy *data, @@ -926,10 +953,11 @@ CURLcode Curl_cpool_add_pollfds(struct cpool *cpool, return result; } -CURLcode Curl_cpool_add_waitfds(struct cpool *cpool, - struct curl_waitfds *cwfds) +/* return information about the shutdown connections */ +unsigned int Curl_cpool_add_waitfds(struct cpool *cpool, + struct Curl_waitfds *cwfds) { - CURLcode result = CURLE_OK; + unsigned int need = 0; CPOOL_LOCK(cpool); if(Curl_llist_head(&cpool->shutdowns)) { @@ -945,14 +973,51 @@ CURLcode Curl_cpool_add_waitfds(struct cpool *cpool, Curl_conn_adjust_pollset(cpool->idata, &ps); Curl_detach_connection(cpool->idata); - result = Curl_waitfds_add_ps(cwfds, &ps); - if(result) - goto out; + need += Curl_waitfds_add_ps(cwfds, &ps); + } + } + CPOOL_UNLOCK(cpool); + return need; +} + +/* return fd_set info about the shutdown connections */ +void Curl_cpool_setfds(struct cpool *cpool, + fd_set *read_fd_set, fd_set *write_fd_set, + int *maxfd) +{ + CPOOL_LOCK(cpool); + if(Curl_llist_head(&cpool->shutdowns)) { + struct Curl_llist_node *e; + + for(e = Curl_llist_head(&cpool->shutdowns); e; + e = Curl_node_next(e)) { + struct easy_pollset ps; + unsigned int i; + struct connectdata *conn = Curl_node_elem(e); + memset(&ps, 0, sizeof(ps)); + Curl_attach_connection(cpool->idata, conn); + Curl_conn_adjust_pollset(cpool->idata, &ps); + Curl_detach_connection(cpool->idata); + + for(i = 0; i < ps.num; i++) { +#if defined(__DJGPP__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warith-conversion" +#endif + if(ps.actions[i] & CURL_POLL_IN) + FD_SET(ps.sockets[i], read_fd_set); + if(ps.actions[i] & CURL_POLL_OUT) + FD_SET(ps.sockets[i], write_fd_set); +#if defined(__DJGPP__) +#pragma GCC diagnostic pop +#endif + if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) && + ((int)ps.sockets[i] > *maxfd)) + *maxfd = (int)ps.sockets[i]; + } } } -out: CPOOL_UNLOCK(cpool); - return result; } static void cpool_perform(struct cpool *cpool) @@ -1052,6 +1117,11 @@ static void cpool_close_and_destroy(struct cpool *cpool, Curl_detach_connection(data); Curl_conn_free(data, conn); + + if(cpool && cpool->multi) { + DEBUGF(infof(data, "[CCACHE] trigger multi connchanged")); + Curl_multi_connchanged(cpool->multi); + } } diff --git a/lib/conncache.h b/lib/conncache.h index a379ee7..5f239bc 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -31,7 +31,7 @@ struct connectdata; struct Curl_easy; struct curl_pollfds; -struct curl_waitfds; +struct Curl_waitfds; struct Curl_multi; struct Curl_share; @@ -113,8 +113,6 @@ typedef bool Curl_cpool_done_match_cb(bool result, void *userdata); * @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. */ @@ -185,8 +183,12 @@ void Curl_cpool_do_locked(struct Curl_easy *data, */ CURLcode Curl_cpool_add_pollfds(struct cpool *connc, struct curl_pollfds *cpfds); -CURLcode Curl_cpool_add_waitfds(struct cpool *connc, - struct curl_waitfds *cwfds); +unsigned int Curl_cpool_add_waitfds(struct cpool *connc, + struct Curl_waitfds *cwfds); + +void Curl_cpool_setfds(struct cpool *cpool, + fd_set *read_fd_set, fd_set *write_fd_set, + int *maxfd); /** * Perform maintenance on connections in the pool. Specifically, diff --git a/lib/connect.c b/lib/connect.c index ee3b4c8..05aabff 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -78,6 +78,7 @@ #include "vquic/vquic.h" /* for quic cfilters */ #include "http_proxy.h" #include "socks.h" +#include "strcase.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -88,6 +89,27 @@ #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) #endif +#if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR) + +enum alpnid Curl_alpn2alpnid(char *name, size_t len) +{ + if(len == 2) { + if(strncasecompare(name, "h1", 2)) + return ALPN_h1; + if(strncasecompare(name, "h2", 2)) + return ALPN_h2; + if(strncasecompare(name, "h3", 2)) + return ALPN_h3; + } + else if(len == 8) { + if(strncasecompare(name, "http/1.1", 8)) + return ALPN_h1; + } + return ALPN_none; /* unknown, probably rubbish input */ +} + +#endif + /* * Curl_timeleft() returns the amount of milliseconds left allowed for the * transfer/connection. If the value is 0, there is no timeout (ie there is @@ -1485,7 +1507,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, DEBUGASSERT(data); DEBUGASSERT(conn->handler); -#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) +#if !defined(CURL_DISABLE_HTTP) if(!conn->cfilter[sockindex] && conn->handler->protocol == CURLPROTO_HTTPS) { DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); @@ -1493,7 +1515,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, if(result) goto out; } -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ +#endif /* !defined(CURL_DISABLE_HTTP) */ /* Still no cfilter set, apply default. */ if(!conn->cfilter[sockindex]) { diff --git a/lib/connect.h b/lib/connect.h index 160db94..b59c38d 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -32,6 +32,8 @@ struct Curl_dns_entry; struct ip_quadruple; +enum alpnid Curl_alpn2alpnid(char *name, size_t len); + /* generic function that returns how much time there is left to run, according to the timeouts set */ timediff_t Curl_timeleft(struct Curl_easy *data, diff --git a/lib/content_encoding.c b/lib/content_encoding.c index a4b16dd..d23f8cd 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -65,34 +65,15 @@ /* allow no more than 5 "chained" compression steps */ #define MAX_ENCODE_STACK 5 - -#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ - +#define DECOMPRESS_BUFFER_SIZE 16384 /* buffer size for decompressed data */ #ifdef HAVE_LIBZ -/* Comment this out if zlib is always going to be at least ver. 1.2.0.4 - (doing so will reduce code size slightly). */ -#define OLD_ZLIB_SUPPORT 1 - -#define GZIP_MAGIC_0 0x1f -#define GZIP_MAGIC_1 0x8b - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ -#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original filename present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define RESERVED 0xE0 /* bits 5..7: reserved */ - typedef enum { ZLIB_UNINIT, /* uninitialized */ ZLIB_INIT, /* initialized */ ZLIB_INFLATING, /* inflating started. */ ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ - ZLIB_GZIP_HEADER, /* reading gzip header */ - ZLIB_GZIP_INFLATING, /* inflating gzip stream */ ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ } zlibInitState; @@ -100,6 +81,7 @@ typedef enum { struct zlib_writer { struct Curl_cwriter super; zlibInitState zlib_init; /* zlib init state */ + char buffer[DECOMPRESS_BUFFER_SIZE]; /* Put the decompressed data here. */ uInt trailerlen; /* Remaining trailer byte count. */ z_stream z; /* State structure for zlib. */ }; @@ -137,9 +119,6 @@ static CURLcode exit_zlib(struct Curl_easy *data, z_stream *z, zlibInitState *zlib_init, CURLcode result) { - if(*zlib_init == ZLIB_GZIP_HEADER) - Curl_safefree(z->next_in); - if(*zlib_init != ZLIB_UNINIT) { if(inflateEnd(z) != Z_OK && result == CURLE_OK) result = process_zlib_error(data, z); @@ -183,21 +162,13 @@ static CURLcode inflate_stream(struct Curl_easy *data, Bytef *orig_in = z->next_in; bool done = FALSE; CURLcode result = CURLE_OK; /* Curl_client_write status */ - char *decomp; /* Put the decompressed data here. */ /* Check state. */ if(zp->zlib_init != ZLIB_INIT && zp->zlib_init != ZLIB_INFLATING && - zp->zlib_init != ZLIB_INIT_GZIP && - zp->zlib_init != ZLIB_GZIP_INFLATING) + zp->zlib_init != ZLIB_INIT_GZIP) return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); - /* Dynamically allocate a buffer for decompression because it is uncommonly - large to hold on the stack */ - decomp = malloc(DSIZ); - if(!decomp) - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - /* because the buffer size is fixed, iteratively decompress and transfer to the client via next_write function. */ while(!done) { @@ -205,8 +176,8 @@ static CURLcode inflate_stream(struct Curl_easy *data, done = TRUE; /* (re)set buffer for decompressed output for every iteration */ - z->next_out = (Bytef *) decomp; - z->avail_out = DSIZ; + z->next_out = (Bytef *) zp->buffer; + z->avail_out = DECOMPRESS_BUFFER_SIZE; #ifdef Z_BLOCK /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */ @@ -217,11 +188,11 @@ static CURLcode inflate_stream(struct Curl_easy *data, #endif /* Flush output data if some. */ - if(z->avail_out != DSIZ) { + if(z->avail_out != DECOMPRESS_BUFFER_SIZE) { if(status == Z_OK || status == Z_STREAM_END) { zp->zlib_init = started; /* Data started. */ - result = Curl_cwriter_write(data, writer->next, type, decomp, - DSIZ - z->avail_out); + result = Curl_cwriter_write(data, writer->next, type, zp->buffer, + DECOMPRESS_BUFFER_SIZE - z->avail_out); if(result) { exit_zlib(data, z, &zp->zlib_init, result); break; @@ -264,7 +235,6 @@ static CURLcode inflate_stream(struct Curl_easy *data, break; } } - free(decomp); /* We are about to leave this call so the `nread' data bytes will not be seen again. If we are in a state that would wrongly allow restart in raw mode @@ -278,7 +248,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Deflate handler. */ static CURLcode deflate_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -294,8 +264,8 @@ static CURLcode deflate_do_init(struct Curl_easy *data, } static CURLcode deflate_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -315,7 +285,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data, } static void deflate_do_close(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -335,124 +305,34 @@ static const struct Curl_cwtype deflate_encoding = { /* Gzip handler. */ static CURLcode gzip_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ + const char *v = zlibVersion(); /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; - if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { - /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ + if(strcmp(v, "1.2.0.4") >= 0) { + /* zlib version >= 1.2.0.4 supports transparent gzip decompressing */ if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { return process_zlib_error(data, z); } zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ } else { - /* we must parse the gzip header and trailer ourselves */ - if(inflateInit2(z, -MAX_WBITS) != Z_OK) { - return process_zlib_error(data, z); - } - zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ - zp->zlib_init = ZLIB_INIT; /* Initial call state */ + failf(data, "too old zlib version: %s", v); + return CURLE_FAILED_INIT; } return CURLE_OK; } -#ifdef OLD_ZLIB_SUPPORT -/* Skip over the gzip header */ -typedef enum { - GZIP_OK, - GZIP_BAD, - GZIP_UNDERFLOW -} gzip_status; - -static gzip_status check_gzip_header(unsigned char const *data, ssize_t len, - ssize_t *headerlen) -{ - int method, flags; - const ssize_t totallen = len; - - /* The shortest header is 10 bytes */ - if(len < 10) - return GZIP_UNDERFLOW; - - if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) - return GZIP_BAD; - - method = data[2]; - flags = data[3]; - - if(method != Z_DEFLATED || (flags & RESERVED) != 0) { - /* cannot handle this compression method or unknown flag */ - return GZIP_BAD; - } - - /* Skip over time, xflags, OS code and all previous bytes */ - len -= 10; - data += 10; - - if(flags & EXTRA_FIELD) { - ssize_t extra_len; - - if(len < 2) - return GZIP_UNDERFLOW; - - extra_len = (data[1] << 8) | data[0]; - - if(len < (extra_len + 2)) - return GZIP_UNDERFLOW; - - len -= (extra_len + 2); - data += (extra_len + 2); - } - - if(flags & ORIG_NAME) { - /* Skip over NUL-terminated filename */ - while(len && *data) { - --len; - ++data; - } - if(!len || *data) - return GZIP_UNDERFLOW; - - /* Skip over the NUL */ - --len; - ++data; - } - - if(flags & COMMENT) { - /* Skip over NUL-terminated comment */ - while(len && *data) { - --len; - ++data; - } - if(!len || *data) - return GZIP_UNDERFLOW; - - /* Skip over the NUL */ - --len; - } - - if(flags & HEAD_CRC) { - if(len < 2) - return GZIP_UNDERFLOW; - - len -= 2; - } - - *headerlen = totallen - len; - return GZIP_OK; -} -#endif - static CURLcode gzip_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -468,117 +348,8 @@ static CURLcode gzip_do_write(struct Curl_easy *data, return inflate_stream(data, writer, type, ZLIB_INIT_GZIP); } -#ifndef OLD_ZLIB_SUPPORT - /* Support for old zlib versions is compiled away and we are running with - an old version, so return an error. */ + /* We are running with an old version: return error. */ return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); - -#else - /* This next mess is to get around the potential case where there is not - * enough data passed in to skip over the gzip header. If that happens, we - * malloc a block and copy what we have then wait for the next call. If - * there still is not enough (this is definitely a worst-case scenario), we - * make the block bigger, copy the next part in and keep waiting. - * - * This is only required with zlib versions < 1.2.0.4 as newer versions - * can handle the gzip header themselves. - */ - - switch(zp->zlib_init) { - /* Skip over gzip header? */ - case ZLIB_INIT: - { - /* Initial call state */ - ssize_t hlen; - - switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { - case GZIP_OK: - z->next_in = (Bytef *) buf + hlen; - z->avail_in = (uInt) (nbytes - hlen); - zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ - break; - - case GZIP_UNDERFLOW: - /* We need more data so we can find the end of the gzip header. it is - * possible that the memory block we malloc here will never be freed if - * the transfer abruptly aborts after this point. Since it is unlikely - * that circumstances will be right for this code path to be followed in - * the first place, and it is even more unlikely for a transfer to fail - * immediately afterwards, it should seldom be a problem. - */ - z->avail_in = (uInt) nbytes; - z->next_in = malloc(z->avail_in); - if(!z->next_in) { - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - } - memcpy(z->next_in, buf, z->avail_in); - zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ - /* We do not have any data to inflate yet */ - return CURLE_OK; - - case GZIP_BAD: - default: - return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); - } - - } - break; - - case ZLIB_GZIP_HEADER: - { - /* Need more gzip header data state */ - ssize_t hlen; - z->avail_in += (uInt) nbytes; - z->next_in = Curl_saferealloc(z->next_in, z->avail_in); - if(!z->next_in) { - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - } - /* Append the new block of data to the previous one */ - memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); - - switch(check_gzip_header(z->next_in, (ssize_t)z->avail_in, &hlen)) { - case GZIP_OK: - /* This is the zlib stream data */ - free(z->next_in); - /* Do not point into the malloced block since we just freed it */ - z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; - z->avail_in = z->avail_in - (uInt)hlen; - zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ - break; - - case GZIP_UNDERFLOW: - /* We still do not have any data to inflate! */ - return CURLE_OK; - - case GZIP_BAD: - default: - return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); - } - - } - break; - - case ZLIB_EXTERNAL_TRAILER: - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - return process_trailer(data, zp); - - case ZLIB_GZIP_INFLATING: - default: - /* Inflating stream state */ - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - break; - } - - if(z->avail_in == 0) { - /* We do not have any data to inflate; wait until next time */ - return CURLE_OK; - } - - /* We have parsed the header, now uncompress the data */ - return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING); -#endif } static void gzip_do_close(struct Curl_easy *data, @@ -601,11 +372,11 @@ static const struct Curl_cwtype gzip_encoding = { #endif /* HAVE_LIBZ */ - #ifdef HAVE_BROTLI /* Brotli writer. */ struct brotli_writer { struct Curl_cwriter super; + char buffer[DECOMPRESS_BUFFER_SIZE]; BrotliDecoderState *br; /* State structure for brotli. */ }; @@ -648,7 +419,7 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be) } static CURLcode brotli_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; @@ -658,12 +429,11 @@ static CURLcode brotli_do_init(struct Curl_easy *data, } static CURLcode brotli_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct brotli_writer *bp = (struct brotli_writer *) writer; const uint8_t *src = (const uint8_t *) buf; - char *decomp; uint8_t *dst; size_t dstleft; CURLcode result = CURLE_OK; @@ -675,18 +445,14 @@ static CURLcode brotli_do_write(struct Curl_easy *data, if(!bp->br) return CURLE_WRITE_ERROR; /* Stream already ended. */ - decomp = malloc(DSIZ); - if(!decomp) - return CURLE_OUT_OF_MEMORY; - while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && result == CURLE_OK) { - dst = (uint8_t *) decomp; - dstleft = DSIZ; + dst = (uint8_t *) bp->buffer; + dstleft = DECOMPRESS_BUFFER_SIZE; r = BrotliDecoderDecompressStream(bp->br, &nbytes, &src, &dstleft, &dst, NULL); result = Curl_cwriter_write(data, writer->next, type, - decomp, DSIZ - dstleft); + bp->buffer, DECOMPRESS_BUFFER_SIZE - dstleft); if(result) break; switch(r) { @@ -704,7 +470,6 @@ static CURLcode brotli_do_write(struct Curl_easy *data, break; } } - free(decomp); return result; } @@ -712,7 +477,6 @@ static void brotli_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; - (void) data; if(bp->br) { @@ -731,30 +495,51 @@ static const struct Curl_cwtype brotli_encoding = { }; #endif - #ifdef HAVE_ZSTD /* Zstd writer. */ struct zstd_writer { struct Curl_cwriter super; ZSTD_DStream *zds; /* State structure for zstd. */ - void *decomp; + char buffer[DECOMPRESS_BUFFER_SIZE]; }; +#ifdef ZSTD_STATIC_LINKING_ONLY +static void *Curl_zstd_alloc(void *opaque, size_t size) +{ + (void)opaque; + return Curl_cmalloc(size); +} + +static void Curl_zstd_free(void *opaque, void *address) +{ + (void)opaque; + Curl_cfree(address); +} +#endif + static CURLcode zstd_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; (void)data; +#ifdef ZSTD_STATIC_LINKING_ONLY + zp->zds = ZSTD_createDStream_advanced((ZSTD_customMem) { + .customAlloc = Curl_zstd_alloc, + .customFree = Curl_zstd_free, + .opaque = NULL + }); +#else zp->zds = ZSTD_createDStream(); - zp->decomp = NULL; +#endif + return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; } static CURLcode zstd_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { CURLcode result = CURLE_OK; struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -765,19 +550,14 @@ static CURLcode zstd_do_write(struct Curl_easy *data, if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); - if(!zp->decomp) { - zp->decomp = malloc(DSIZ); - if(!zp->decomp) - return CURLE_OUT_OF_MEMORY; - } in.pos = 0; in.src = buf; in.size = nbytes; for(;;) { out.pos = 0; - out.dst = zp->decomp; - out.size = DSIZ; + out.dst = zp->buffer; + out.size = DECOMPRESS_BUFFER_SIZE; errorCode = ZSTD_decompressStream(zp->zds, &out, &in); if(ZSTD_isError(errorCode)) { @@ -785,7 +565,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, } if(out.pos > 0) { result = Curl_cwriter_write(data, writer->next, type, - zp->decomp, out.pos); + zp->buffer, out.pos); if(result) break; } @@ -800,13 +580,8 @@ static void zstd_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; - (void)data; - if(zp->decomp) { - free(zp->decomp); - zp->decomp = NULL; - } if(zp->zds) { ZSTD_freeDStream(zp->zds); zp->zds = NULL; @@ -823,7 +598,6 @@ static const struct Curl_cwtype zstd_encoding = { }; #endif - /* Identity handler. */ static const struct Curl_cwtype identity_encoding = { "identity", @@ -834,7 +608,6 @@ static const struct Curl_cwtype identity_encoding = { sizeof(struct Curl_cwriter) }; - /* supported general content decoders. */ static const struct Curl_cwtype * const general_unencoders[] = { &identity_encoding, @@ -898,7 +671,7 @@ void Curl_all_content_encodings(char *buf, size_t blen) /* Deferred error dummy writer. */ static CURLcode error_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { (void)data; (void)writer; @@ -906,8 +679,8 @@ static CURLcode error_do_init(struct Curl_easy *data, } static CURLcode error_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { (void) writer; (void) buf; @@ -1082,5 +855,4 @@ void Curl_all_content_encodings(char *buf, size_t blen) strcpy(buf, CONTENT_ENCODING_DEFAULT); } - #endif /* CURL_DISABLE_HTTP */ diff --git a/lib/cookie.c b/lib/cookie.c index 773e535..6fe949b 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -97,6 +97,26 @@ Example set of cookies: static void strstore(char **str, const char *newstr, size_t len); +/* number of seconds in 400 days */ +#define COOKIES_MAXAGE (400*24*3600) + +/* Make sure cookies never expire further away in time than 400 days into the + future. (from RFC6265bis draft-19) + + For the sake of easier testing, align the capped time to an even 60 second + boundary. +*/ +static void cap_expires(time_t now, struct Cookie *co) +{ + if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) { + timediff_t cap = now + COOKIES_MAXAGE; + if(co->expires > cap) { + cap += 30; + co->expires = (cap/60)*60; + } + } +} + static void freecookie(struct Cookie *co) { free(co->domain); @@ -438,7 +458,7 @@ static bool bad_domain(const char *domain, size_t len) fine. The prime reason for filtering out control bytes is that some HTTP servers return 400 for requests that contain such. */ -static int invalid_octets(const char *p) +static bool invalid_octets(const char *p) { /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ static const char badoctets[] = { @@ -449,7 +469,7 @@ static int invalid_octets(const char *p) size_t len; /* scan for all the octets that are *not* in cookie-octet */ len = strcspn(p, badoctets); - return (p[len] != '\0'); + return p[len] != '\0'; } #define CERR_OK 0 @@ -469,6 +489,13 @@ static int invalid_octets(const char *p) #define CERR_PSL 14 /* a public suffix */ #define CERR_LIVE_WINS 15 +/* The maximum length we accept a date string for the 'expire' keyword. The + standard date formats are within the 30 bytes range. This adds an extra + margin just to make sure it realistically works with what is used out + there. +*/ +#define MAX_DATE_LENGTH 80 + static int parse_cookie_header(struct Curl_easy *data, struct Cookie *co, @@ -707,16 +734,20 @@ parse_cookie_header(struct Curl_easy *data, co->expires += now; break; } + cap_expires(now, co); } else if((nlen == 7) && strncasecompare("expires", namep, 7)) { - if(!co->expires) { + if(!co->expires && (vlen < MAX_DATE_LENGTH)) { /* * Let max-age have priority. * * If the date cannot get parsed for whatever reason, the cookie * will be treated as a session cookie */ - co->expires = Curl_getdate_capped(valuep); + char dbuf[MAX_DATE_LENGTH + 1]; + memcpy(dbuf, valuep, vlen); + dbuf[vlen] = 0; + co->expires = Curl_getdate_capped(dbuf); /* * Session cookies have expires set to 0 so if we get that back @@ -727,6 +758,7 @@ parse_cookie_header(struct Curl_easy *data, co->expires = 1; else if(co->expires < 0) co->expires = 0; + cap_expires(now, co); } } @@ -805,10 +837,9 @@ parse_netscape(struct Cookie *co, * This line is NOT an HTTP header style line, we do offer support for * reading the odd netscape cookies-file format here */ - char *ptr; - char *firstptr; - char *tok_buf = NULL; + const char *ptr, *next; int fields; + size_t len; /* * In 2008, Internet Explorer introduced HTTP-only cookies to prevent XSS @@ -825,29 +856,22 @@ parse_netscape(struct Cookie *co, /* do not even try the comments */ return CERR_COMMENT; - /* strip off the possible end-of-line characters */ - ptr = strchr(lineptr, '\r'); - if(ptr) - *ptr = 0; /* clear it */ - ptr = strchr(lineptr, '\n'); - if(ptr) - *ptr = 0; /* clear it */ - - /* tokenize on TAB */ - firstptr = Curl_strtok_r((char *)lineptr, "\t", &tok_buf); - /* * Now loop through the fields and init the struct we already have * allocated */ fields = 0; - for(ptr = firstptr; ptr; - ptr = Curl_strtok_r(NULL, "\t", &tok_buf), fields++) { + for(next = lineptr; next; fields++) { + ptr = next; + len = strcspn(ptr, "\t\r\n"); + next = (ptr[len] == '\t' ? &ptr[len + 1] : NULL); switch(fields) { case 0: - if(ptr[0]=='.') /* skip preceding dots */ + if(ptr[0]=='.') { /* skip preceding dots */ ptr++; - co->domain = strdup(ptr); + len--; + } + co->domain = Curl_memdup0(ptr, len); if(!co->domain) return CERR_OUT_OF_MEMORY; break; @@ -857,13 +881,13 @@ parse_netscape(struct Cookie *co, * domain can access the variable. Set TRUE when the cookie says * .domain.com and to false when the domain is complete www.domain.com */ - co->tailmatch = !!strcasecompare(ptr, "TRUE"); + co->tailmatch = !!strncasecompare(ptr, "TRUE", len); break; case 2: /* The file format allows the path field to remain not filled in */ - if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { + if(strncmp("TRUE", ptr, len) && strncmp("FALSE", ptr, len)) { /* only if the path does not look like a boolean option! */ - co->path = strdup(ptr); + co->path = Curl_memdup0(ptr, len); if(!co->path) return CERR_OUT_OF_MEMORY; else { @@ -884,7 +908,7 @@ parse_netscape(struct Cookie *co, FALLTHROUGH(); case 3: co->secure = FALSE; - if(strcasecompare(ptr, "TRUE")) { + if(strncasecompare(ptr, "TRUE", len)) { if(secure || ci->running) co->secure = TRUE; else @@ -892,11 +916,19 @@ parse_netscape(struct Cookie *co, } break; case 4: - if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) - return CERR_RANGE; + { + char *endp; + const char *p; + /* make sure curlx_strtoofft won't read past the current field */ + for(p = ptr; p < &ptr[len] && ISDIGIT(*p); ++p) + ; + if(p == ptr || p != &ptr[len] || + curlx_strtoofft(ptr, &endp, 10, &co->expires) || endp != &ptr[len]) + return CERR_RANGE; + } break; case 5: - co->name = strdup(ptr); + co->name = Curl_memdup0(ptr, len); if(!co->name) return CERR_OUT_OF_MEMORY; else { @@ -908,7 +940,7 @@ parse_netscape(struct Cookie *co, } break; case 6: - co->value = strdup(ptr); + co->value = Curl_memdup0(ptr, len); if(!co->value) return CERR_OUT_OF_MEMORY; break; @@ -1303,14 +1335,14 @@ static int cookie_sort(const void *p1, const void *p2) l2 = c2->path ? strlen(c2->path) : 0; if(l1 != l2) - return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ + return (l2 > l1) ? 1 : -1; /* avoid size_t <=> int conversions */ /* 2 - compare cookie domain lengths */ l1 = c1->domain ? strlen(c1->domain) : 0; l2 = c2->domain ? strlen(c2->domain) : 0; if(l1 != l2) - return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ + return (l2 > l1) ? 1 : -1; /* avoid size_t <=> int conversions */ /* 3 - compare cookie name lengths */ l1 = c1->name ? strlen(c1->name) : 0; diff --git a/lib/cookie.h b/lib/cookie.h index c98f360..7af6507 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -38,7 +38,7 @@ struct Cookie { char *spath; /* sanitized cookie path */ char *domain; /* domain = */ curl_off_t expires; /* expires = */ - int creationtime; /* time when the cookie was written */ + unsigned int creationtime; /* time when the cookie was written */ BIT(tailmatch); /* tail-match the domain name */ BIT(secure); /* the 'secure' keyword was used */ BIT(livecookie); /* updated from a server, not a stored file */ @@ -60,10 +60,10 @@ struct CookieInfo { /* linked lists of cookies we know of */ struct Curl_llist cookielist[COOKIE_HASH_SIZE]; curl_off_t next_expiration; /* the next time at which expiration happens */ - int numcookies; /* number of cookies in the "jar" */ - int lastct; /* last creation-time used in the jar */ - bool running; /* state info, for cookie adding information */ - bool newsession; /* new session, discard session cookies on load */ + unsigned int numcookies; /* number of cookies in the "jar" */ + unsigned int lastct; /* last creation-time used in the jar */ + BIT(running); /* state info, for cookie adding information */ + BIT(newsession); /* new session, discard session cookies on load */ }; /* The maximum sizes we accept for cookies. RFC 6265 section 6.1 says diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index a08caf3..52f0f91 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -318,11 +318,7 @@ Curl_he2ai(const struct hostent *he, int port) addr = (void *)ai->ai_addr; /* storage area for this info */ memcpy(&addr->sin_addr, curr, sizeof(struct in_addr)); -#ifdef __MINGW32__ - addr->sin_family = (short)(he->h_addrtype); -#else addr->sin_family = (CURL_SA_FAMILY_T)(he->h_addrtype); -#endif addr->sin_port = htons((unsigned short)port); break; @@ -331,11 +327,7 @@ Curl_he2ai(const struct hostent *he, int port) addr6 = (void *)ai->ai_addr; /* storage area for this info */ memcpy(&addr6->sin6_addr, curr, sizeof(struct in6_addr)); -#ifdef __MINGW32__ - addr6->sin6_family = (short)(he->h_addrtype); -#else addr6->sin6_family = (CURL_SA_FAMILY_T)(he->h_addrtype); -#endif addr6->sin6_port = htons((unsigned short)port); break; #endif diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index 1ac59ff..a41af0c 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -565,9 +565,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_FILIO_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_WAIT_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_IOCTL_H 1 @@ -625,8 +622,8 @@ /* Define this symbol if your OS supports changing the contents of argv */ #cmakedefine HAVE_WRITABLE_ARGV 1 -/* Define to 1 if you need the malloc.h header file even with stdlib.h */ -#cmakedefine NEED_MALLOC_H 1 +/* Define this if time_t is unsigned */ +#cmakedefine HAVE_TIME_T_UNSIGNED 1 /* Define to 1 if _REENTRANT preprocessor symbol must be defined. */ #cmakedefine NEED_REENTRANT 1 @@ -634,24 +631,6 @@ /* cpu-machine-OS */ #cmakedefine CURL_OS ${CURL_OS} -/* Name of package */ -#cmakedefine PACKAGE ${PACKAGE} - -/* Define to the address where bug reports for this package should be sent. */ -#cmakedefine PACKAGE_BUGREPORT ${PACKAGE_BUGREPORT} - -/* Define to the full name of this package. */ -#cmakedefine PACKAGE_NAME ${PACKAGE_NAME} - -/* Define to the full name and version of this package. */ -#cmakedefine PACKAGE_STRING ${PACKAGE_STRING} - -/* Define to the one symbol short name of this package. */ -#cmakedefine PACKAGE_TARNAME ${PACKAGE_TARNAME} - -/* Define to the version of this package. */ -#cmakedefine PACKAGE_VERSION ${PACKAGE_VERSION} - /* Note: SIZEOF_* variables are fetched with CMake through check_type_size(). As per CMake documentation on CheckTypeSize, C preprocessor code is @@ -703,6 +682,9 @@ ${SIZEOF_TIME_T_CODE} /* if Secure Transport is enabled */ #cmakedefine USE_SECTRANSP 1 +/* if SSL session export support is available */ +#cmakedefine USE_SSLS_EXPORT 1 + /* if mbedTLS is enabled */ #cmakedefine USE_MBEDTLS 1 @@ -742,6 +724,9 @@ ${SIZEOF_TIME_T_CODE} /* if OpenSSL is in use */ #cmakedefine USE_OPENSSL 1 +/* if AmiSSL is in use */ +#cmakedefine USE_AMISSL 1 + /* if librtmp/rtmpdump is in use */ #cmakedefine USE_LIBRTMP 1 @@ -791,12 +776,12 @@ ${SIZEOF_TIME_T_CODE} /* to enable Windows SSL */ #cmakedefine USE_SCHANNEL 1 +/* if Watt-32 is in use */ +#cmakedefine USE_WATT32 1 + /* enable multiple SSL backends */ #cmakedefine CURL_WITH_MULTI_SSL 1 -/* Version number of package */ -#cmakedefine VERSION ${VERSION} - /* Number of bits in a file offset, on hosts where this is settable. */ #cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} @@ -841,3 +826,9 @@ ${SIZEOF_TIME_T_CODE} /* if ECH support is available */ #cmakedefine USE_ECH 1 + +/* Define to 1 if you have the wolfSSL_CTX_GenerateEchConfig function. */ +#cmakedefine HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + +/* Define to 1 if you have the SSL_set1_ech_config_list function. */ +#cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST diff --git a/lib/curl_ctype.h b/lib/curl_ctype.h index 7f0d0cc..b70acf3 100644 --- a/lib/curl_ctype.h +++ b/lib/curl_ctype.h @@ -46,6 +46,6 @@ #define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \ ((x) == '~')) #define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x)) - +#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') #endif /* HEADER_CURL_CTYPE_H */ diff --git a/lib/curl_get_line.c b/lib/curl_get_line.c index 1002073..7ee3b65 100644 --- a/lib/curl_get_line.c +++ b/lib/curl_get_line.c @@ -28,7 +28,9 @@ !defined(CURL_DISABLE_HSTS) || !defined(CURL_DISABLE_NETRC) #include "curl_get_line.h" +#ifdef BUILDING_LIBCURL #include "curl_memory.h" +#endif /* The last #include file should be: */ #include "memdebug.h" diff --git a/lib/curl_get_line.h b/lib/curl_get_line.h index 7907cde..1e3b0f0 100644 --- a/lib/curl_get_line.h +++ b/lib/curl_get_line.h @@ -26,6 +26,12 @@ #include "dynbuf.h" +#ifndef BUILDING_LIBCURL +/* this renames functions so that the tool code can use the same code + without getting symbol collisions */ +#define Curl_get_line(a,b) curlx_get_line(a,b) +#endif + /* Curl_get_line() returns complete lines that end with a newline. */ int Curl_get_line(struct dynbuf *buf, FILE *input); diff --git a/lib/curl_gethostname.c b/lib/curl_gethostname.c index 617a8ad..fb418b4 100644 --- a/lib/curl_gethostname.c +++ b/lib/curl_gethostname.c @@ -73,7 +73,11 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen) #else /* DEBUGBUILD */ name[0] = '\0'; +#ifdef __AMIGA__ + err = gethostname((unsigned char *)name, namelen); +#else err = gethostname(name, namelen); +#endif #endif diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index b7e774c..0b383f5 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -40,6 +40,11 @@ #define CURL_ALIGN8 #endif +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = { 6, (char *)"\x2b\x06\x01\x05\x05\x02" }; @@ -149,4 +154,8 @@ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, #endif } +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + #endif /* HAVE_GSSAPI */ diff --git a/lib/curl_hmac.h b/lib/curl_hmac.h index ed5035c..f54edeb 100644 --- a/lib/curl_hmac.h +++ b/lib/curl_hmac.h @@ -24,9 +24,9 @@ * ***************************************************************************/ -#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ - || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ - || defined(USE_LIBSSH2) +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ + !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ + defined(USE_SSL) #include diff --git a/lib/curl_multibyte.c b/lib/curl_multibyte.c index 86ac74f..220b2fa 100644 --- a/lib/curl_multibyte.c +++ b/lib/curl_multibyte.c @@ -32,7 +32,7 @@ #include "curl_setup.h" -#if defined(_WIN32) +#ifdef _WIN32 #include "curl_multibyte.h" @@ -84,16 +84,170 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w) return str_utf8; } -#endif /* _WIN32 */ +/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */ +#if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \ + (_WIN32_WINNT < _WIN32_WINNT_WIN10) +WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *); +#endif + +/* Fix excessive paths (paths that exceed MAX_PATH length of 260). + * + * This is a helper function to fix paths that would exceed the MAX_PATH + * limitation check done by Windows APIs. It does so by normalizing the passed + * in filename or path 'in' to its full canonical path, and if that path is + * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path. + * + * For example 'in' filename255chars in current directory C:\foo\bar is + * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows + * it is ok to access that filename even though the actual full path is longer + * than 260 chars. + * + * For non-Unicode builds this function may fail sometimes because only the + * Unicode versions of some Windows API functions can access paths longer than + * MAX_PATH, for example GetFullPathNameW which is used in this function. When + * the full path is then converted from Unicode to multibyte that fails if any + * directories in the path contain characters not in the current codepage. + */ +static bool fix_excessive_path(const TCHAR *in, TCHAR **out) +{ + size_t needed, count; + const wchar_t *in_w; + wchar_t *fbuf = NULL; + + /* MS documented "approximate" limit for the maximum path length */ + const size_t max_path_len = 32767; + +#ifndef _UNICODE + wchar_t *ibuf = NULL; + char *obuf = NULL; +#endif + + *out = NULL; + + /* skip paths already normalized */ + if(!_tcsncmp(in, _T("\\\\?\\"), 4)) + goto cleanup; + +#ifndef _UNICODE + /* convert multibyte input to unicode */ + needed = mbstowcs(NULL, in, 0); + if(needed == (size_t)-1 || needed >= max_path_len) + goto cleanup; + ++needed; /* for NUL */ + ibuf = malloc(needed * sizeof(wchar_t)); + if(!ibuf) + goto cleanup; + count = mbstowcs(ibuf, in, needed); + if(count == (size_t)-1 || count >= needed) + goto cleanup; + in_w = ibuf; +#else + in_w = in; +#endif + + /* GetFullPathNameW returns the normalized full path in unicode. It converts + forward slashes to backslashes, processes .. to remove directory segments, + etc. Unlike GetFullPathNameA it can process paths that exceed MAX_PATH. */ + needed = (size_t)GetFullPathNameW(in_w, 0, NULL, NULL); + if(!needed || needed > max_path_len) + goto cleanup; + /* skip paths that are not excessive and do not need modification */ + if(needed <= MAX_PATH) + goto cleanup; + fbuf = malloc(needed * sizeof(wchar_t)); + if(!fbuf) + goto cleanup; + count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL); + if(!count || count >= needed) + goto cleanup; + + /* prepend \\?\ or \\?\UNC\ to the excessively long path. + * + * c:\longpath ---> \\?\c:\longpath + * \\.\c:\longpath ---> \\?\c:\longpath + * \\?\c:\longpath ---> \\?\c:\longpath (unchanged) + * \\server\c$\longpath ---> \\?\UNC\server\c$\longpath + * + * https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats + */ + if(!wcsncmp(fbuf, L"\\\\?\\", 4)) + ; /* do nothing */ + else if(!wcsncmp(fbuf, L"\\\\.\\", 4)) + fbuf[2] = '?'; + else if(!wcsncmp(fbuf, L"\\\\.", 3) || !wcsncmp(fbuf, L"\\\\?", 3)) { + /* Unexpected, not UNC. The formatting doc doesn't allow this AFAICT. */ + goto cleanup; + } + else { + wchar_t *temp; + + if(!wcsncmp(fbuf, L"\\\\", 2)) { + /* "\\?\UNC\" + full path without "\\" + null */ + needed = 8 + (count - 2) + 1; + if(needed > max_path_len) + goto cleanup; + + temp = malloc(needed * sizeof(wchar_t)); + if(!temp) + goto cleanup; -#if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES) + wcsncpy(temp, L"\\\\?\\UNC\\", 8); + wcscpy(temp + 8, fbuf + 2); + } + else { + /* "\\?\" + full path + null */ + needed = 4 + count + 1; + if(needed > max_path_len) + goto cleanup; + + temp = malloc(needed * sizeof(wchar_t)); + if(!temp) + goto cleanup; + + wcsncpy(temp, L"\\\\?\\", 4); + wcscpy(temp + 4, fbuf); + } + + free(fbuf); + fbuf = temp; + } + +#ifndef _UNICODE + /* convert unicode full path to multibyte output */ + needed = wcstombs(NULL, fbuf, 0); + if(needed == (size_t)-1 || needed >= max_path_len) + goto cleanup; + ++needed; /* for NUL */ + obuf = malloc(needed); + if(!obuf) + goto cleanup; + count = wcstombs(obuf, fbuf, needed); + if(count == (size_t)-1 || count >= needed) + goto cleanup; + *out = obuf; + obuf = NULL; +#else + *out = fbuf; + fbuf = NULL; +#endif + +cleanup: + free(fbuf); +#ifndef _UNICODE + free(ibuf); + free(obuf); +#endif + return *out ? true : false; +} int curlx_win32_open(const char *filename, int oflag, ...) { int pmode = 0; + int result = -1; + TCHAR *fixed = NULL; + const TCHAR *target = NULL; #ifdef _UNICODE - int result = -1; wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename); #endif @@ -105,58 +259,95 @@ int curlx_win32_open(const char *filename, int oflag, ...) #ifdef _UNICODE if(filename_w) { - result = _wopen(filename_w, oflag, pmode); + if(fix_excessive_path(filename_w, &fixed)) + target = fixed; + else + target = filename_w; + result = _wopen(target, oflag, pmode); curlx_unicodefree(filename_w); } else errno = EINVAL; - return result; #else - return (_open)(filename, oflag, pmode); + if(fix_excessive_path(filename, &fixed)) + target = fixed; + else + target = filename; + result = (_open)(target, oflag, pmode); #endif + + free(fixed); + return result; } FILE *curlx_win32_fopen(const char *filename, const char *mode) { -#ifdef _UNICODE FILE *result = NULL; + TCHAR *fixed = NULL; + const TCHAR *target = NULL; + +#ifdef _UNICODE wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename); wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode); - if(filename_w && mode_w) - result = _wfopen(filename_w, mode_w); + if(filename_w && mode_w) { + if(fix_excessive_path(filename_w, &fixed)) + target = fixed; + else + target = filename_w; + result = _wfopen(target, mode_w); + } else errno = EINVAL; curlx_unicodefree(filename_w); curlx_unicodefree(mode_w); - return result; #else - return (fopen)(filename, mode); + if(fix_excessive_path(filename, &fixed)) + target = fixed; + else + target = filename; + result = (fopen)(target, mode); #endif + + free(fixed); + return result; } int curlx_win32_stat(const char *path, struct_stat *buffer) { -#ifdef _UNICODE int result = -1; + TCHAR *fixed = NULL; + const TCHAR *target = NULL; + +#ifdef _UNICODE wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); if(path_w) { -#if defined(USE_WIN32_SMALL_FILES) - result = _wstat(path_w, buffer); + if(fix_excessive_path(path_w, &fixed)) + target = fixed; + else + target = path_w; +#ifndef USE_WIN32_LARGE_FILES + result = _wstat(target, buffer); #else - result = _wstati64(path_w, buffer); + result = _wstati64(target, buffer); #endif curlx_unicodefree(path_w); } else errno = EINVAL; - return result; #else -#if defined(USE_WIN32_SMALL_FILES) - return _stat(path, buffer); + if(fix_excessive_path(path, &fixed)) + target = fixed; + else + target = path; +#ifndef USE_WIN32_LARGE_FILES + result = _stat(target, buffer); #else - return _stati64(path, buffer); + result = _stati64(target, buffer); #endif #endif + + free(fixed); + return result; } -#endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */ +#endif /* _WIN32 */ diff --git a/lib/curl_rtmp.c b/lib/curl_rtmp.c index 59fcc4e..caa92e7 100644 --- a/lib/curl_rtmp.c +++ b/lib/curl_rtmp.c @@ -85,6 +85,7 @@ const struct Curl_handler Curl_handler_rtmp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_RTMP, /* defport */ CURLPROTO_RTMP, /* protocol */ CURLPROTO_RTMP, /* family */ @@ -109,6 +110,7 @@ const struct Curl_handler Curl_handler_rtmpt = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_RTMPT, /* defport */ CURLPROTO_RTMPT, /* protocol */ CURLPROTO_RTMPT, /* family */ @@ -133,6 +135,7 @@ const struct Curl_handler Curl_handler_rtmpe = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_RTMP, /* defport */ CURLPROTO_RTMPE, /* protocol */ CURLPROTO_RTMPE, /* family */ @@ -157,6 +160,7 @@ const struct Curl_handler Curl_handler_rtmpte = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_RTMPT, /* defport */ CURLPROTO_RTMPTE, /* protocol */ CURLPROTO_RTMPTE, /* family */ @@ -181,6 +185,7 @@ const struct Curl_handler Curl_handler_rtmps = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_RTMPS, /* defport */ CURLPROTO_RTMPS, /* protocol */ CURLPROTO_RTMP, /* family */ @@ -205,6 +210,7 @@ const struct Curl_handler Curl_handler_rtmpts = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_RTMPS, /* defport */ CURLPROTO_RTMPTS, /* protocol */ CURLPROTO_RTMPT, /* family */ diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 50b4479..f4e5400 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -31,13 +31,6 @@ /* 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) -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic ignored "-Wsign-conversion" -#endif -#endif - /* Set default _WIN32_WINNT */ #ifdef __MINGW32__ #include <_mingw.h> @@ -85,13 +78,17 @@ #endif #endif -/* - * Disable Visual Studio warnings: - * 4127 "conditional expression is constant" - */ #ifdef _MSC_VER +/* Disable Visual Studio warnings: 4127 "conditional expression is constant" */ #pragma warning(disable:4127) +/* Avoid VS2005 and upper complaining about portable C functions. */ +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE /* for strdup(), write(), etc. */ +#endif +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE /* for fopen(), getenv(), etc. */ #endif +#endif /* _MSC_VER */ #ifdef _WIN32 /* @@ -100,26 +97,26 @@ * Make sure to define this macro before including any Windows headers. */ # ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN # endif # ifndef NOGDI -# define 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 -# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ - !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) -# define CURL_WINDOWS_UWP +# if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \ + defined(WINAPI_FAMILY) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define CURL_WINDOWS_UWP +# endif # endif -# endif #endif /* Compatibility */ -#if defined(ENABLE_IPV6) -# define USE_IPV6 1 +#ifdef ENABLE_IPV6 +#define USE_IPV6 1 #endif /* @@ -133,12 +130,8 @@ #else /* HAVE_CONFIG_H */ -#ifdef _WIN32_WCE -# include "config-win32ce.h" -#else -# ifdef _WIN32 -# include "config-win32.h" -# endif +#ifdef _WIN32 +# include "config-win32.h" #endif #ifdef macintosh @@ -149,10 +142,6 @@ # include "config-riscos.h" #endif -#ifdef __AMIGA__ -# include "config-amigaos.h" -#endif - #ifdef __OS400__ # include "config-os400.h" #endif @@ -161,10 +150,6 @@ # include "config-plan9.h" #endif -#ifdef MSDOS -# include "config-dos.h" -#endif - #endif /* HAVE_CONFIG_H */ /* ================================================================ */ @@ -183,7 +168,7 @@ #ifdef NEED_THREAD_SAFE # ifndef _THREAD_SAFE -# define _THREAD_SAFE +# define _THREAD_SAFE # endif #endif @@ -195,14 +180,14 @@ #ifdef NEED_REENTRANT # ifndef _REENTRANT -# define _REENTRANT +# define _REENTRANT # endif #endif /* Solaris needs this to get a POSIX-conformant getpwuid_r */ #if defined(sun) || defined(__sun) # ifndef _POSIX_PTHREAD_SEMANTICS -# define _POSIX_PTHREAD_SEMANTICS 1 +# define _POSIX_PTHREAD_SEMANTICS 1 # endif #endif @@ -290,14 +275,6 @@ # define CURL_DISABLE_HTTP_AUTH 1 #endif -/* - * ECH requires HTTPSRR. - */ - -#if defined(USE_ECH) && !defined(USE_HTTPSRR) -# define USE_HTTPSRR -#endif - /* ================================================================ */ /* No system header file shall be included in this file before this */ /* point. */ @@ -393,17 +370,23 @@ # endif #endif +#ifdef USE_ARES +# ifndef CARES_NO_DEPRECATED +# define CARES_NO_DEPRECATED /* for ares_getsock() */ +# endif +# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && defined(_WIN32) +# define CARES_STATICLIB /* define it before including ares.h */ +# endif +#endif + #ifdef USE_LWIPSOCK # include # include # include #endif -#ifdef HAVE_EXTRA_STRICMP_H +#ifdef macintosh # include -#endif - -#ifdef HAVE_EXTRA_STRDUP_H # include #endif @@ -456,9 +439,9 @@ #include #ifdef __TANDEM /* for ns*-tandem-nsk systems */ -# if ! defined __LP64 -# include /* FLOSS is only used for 32-bit builds. */ -# endif +# if ! defined __LP64 +# include /* FLOSS is only used for 32-bit builds. */ +# endif #endif #ifndef STDC_HEADERS /* no standard C headers! */ @@ -494,11 +477,19 @@ FILE *curlx_win32_fopen(const char *filename, const char *mode); #endif +#ifdef __DJGPP__ +/* Requires DJGPP 2.04 */ +# include +# undef lseek +# define lseek(fdes,offset,whence) llseek(fdes, offset, whence) +# define LSEEK_ERROR (offset_t)-1 +#endif + /* * Small file (<2Gb) support using Win32 functions. */ -#ifdef USE_WIN32_SMALL_FILES +#if defined(_WIN32) && !defined(USE_WIN32_LARGE_FILES) # include # include # include @@ -518,11 +509,11 @@ #endif #ifndef struct_stat -# define struct_stat struct stat +#define struct_stat struct stat #endif #ifndef LSEEK_ERROR -# define LSEEK_ERROR (off_t)-1 +#define LSEEK_ERROR (off_t)-1 #endif #ifndef SIZEOF_TIME_T @@ -593,8 +584,8 @@ # endif # define CURL_UINT64_SUFFIX CURL_SUFFIX_CURL_OFF_TU # define CURL_UINT64_C(val) CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX) -# define FMT_PRId64 CURL_FORMAT_CURL_OFF_T -# define FMT_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 @@ -693,9 +684,9 @@ /* * MSVC threads support requires a multi-threaded runtime library. * _beginthreadex() is not available in single-threaded ones. + * Single-threaded option was last available in VS2005: _MSC_VER <= 1400 */ - -#if defined(_MSC_VER) && !defined(_MT) +#if defined(_MSC_VER) && !defined(_MT) /* available in _MSC_VER <= 1400 */ # undef USE_THREADS_POSIX # undef USE_THREADS_WIN32 #endif @@ -713,15 +704,15 @@ # define CURLRES_IPV4 #endif -#ifdef USE_ARES +#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +# define CURLRES_ASYNCH +# define CURLRES_THREADED +#elif defined(USE_ARES) # define CURLRES_ASYNCH # define CURLRES_ARES /* now undef the stock libc functions just to avoid them being used */ # undef HAVE_GETADDRINFO # undef HAVE_FREEADDRINFO -#elif defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) -# define CURLRES_ASYNCH -# define CURLRES_THREADED #else # define CURLRES_SYNCH #endif @@ -738,14 +729,23 @@ #error "libidn2 cannot be enabled with WinIDN or AppleIDN, choose one." #endif -#define LIBIDN_REQUIRED_VERSION "0.4.1" - #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ defined(USE_BEARSSL) || defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif +#if defined(USE_OPENSSL) && defined(USE_WOLFSSL) +# include +# if LIBWOLFSSL_VERSION_HEX >= 0x05007006 +# ifndef OPENSSL_COEXIST +# define OPENSSL_COEXIST +# endif +# else +# error "OpenSSL can only coexist with wolfSSL v5.7.6 or upper" +# endif +#endif + #if defined(USE_WOLFSSL) && defined(USE_GNUTLS) /* Avoid defining unprefixed wolfSSL SHA macros colliding with nettle ones */ #define NO_OLD_WC_NAMES @@ -776,10 +776,6 @@ # endif #endif -#ifdef CURL_WANTS_CA_BUNDLE_ENV -#error "No longer supported. Set CURLOPT_CAINFO at runtime instead." -#endif - #if defined(USE_LIBSSH2) || defined(USE_LIBSSH) || defined(USE_WOLFSSH) #define USE_SSH #endif @@ -812,7 +808,7 @@ #if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) || \ defined(__IAR_SYSTEMS_ICC__) # define CURL_NORETURN __attribute__((__noreturn__)) -#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +#elif defined(_MSC_VER) # define CURL_NORETURN __declspec(noreturn) #else # define CURL_NORETURN @@ -843,7 +839,7 @@ */ #ifndef Curl_nop_stmt -# define Curl_nop_stmt do { } while(0) +#define Curl_nop_stmt do { } while(0) #endif /* @@ -882,6 +878,14 @@ #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif +/* Since O_BINARY is used in bitmasks, setting it to zero makes it usable in + source code but yet it does not ruin anything */ +#ifdef O_BINARY +#define CURL_O_BINARY O_BINARY +#else +#define CURL_O_BINARY 0 +#endif + /* In Windows the default file mode is text but an application can override it. Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 */ @@ -911,6 +915,8 @@ endings either CRLF or LF so 't' is appropriate. # define CURL_SA_FAMILY_T sa_family_t # elif defined(HAVE_ADDRESS_FAMILY) # define CURL_SA_FAMILY_T ADDRESS_FAMILY +# elif defined(__AMIGA__) +# define CURL_SA_FAMILY_T unsigned char # else /* use a sensible default */ # define CURL_SA_FAMILY_T unsigned short @@ -927,8 +933,9 @@ endings either CRLF or LF so 't' is appropriate. as their argument */ #define STRCONST(x) x,sizeof(x)-1 -/* Some versions of the Android SDK is missing the declaration */ -#if defined(HAVE_GETPWUID_R) && defined(HAVE_DECL_GETPWUID_R_MISSING) +/* Some versions of the Android NDK is missing the declaration */ +#if defined(HAVE_GETPWUID_R) && \ + defined(__ANDROID_API__) && (__ANDROID_API__ < 21) struct passwd; int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result); @@ -940,8 +947,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define UNITTEST static #endif -/* Hyper supports HTTP2 also, but Curl's integration with Hyper does not */ -#if defined(USE_NGHTTP2) +#ifdef USE_NGHTTP2 #define USE_HTTP2 #endif @@ -950,7 +956,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, defined(USE_QUICHE) || defined(USE_MSH3) #ifdef CURL_WITH_MULTI_SSL -#error "Multi-SSL combined with QUIC is not supported" +#error "MultiSSL combined with QUIC is not supported" #endif #define USE_HTTP3 @@ -970,7 +976,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, # define UNIX_PATH_MAX 108 /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct sockaddr_un { - ADDRESS_FAMILY sun_family; + CURL_SA_FAMILY_T sun_family; char sun_path[UNIX_PATH_MAX]; } SOCKADDR_UN, *PSOCKADDR_UN; # define WIN32_SOCKADDR_UN @@ -983,26 +989,26 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define OPENSSL_SUPPRESS_DEPRECATED #endif -#if defined(inline) - /* 'inline' is defined as macro and assumed to be correct */ - /* No need for 'inline' replacement */ +#if defined(CURL_INLINE) +/* 'CURL_INLINE' defined, use as-is */ +#elif defined(inline) +# define CURL_INLINE inline /* 'inline' defined, assumed correct */ #elif defined(__cplusplus) - /* The code is compiled with C++ compiler. - C++ always supports 'inline'. */ - /* No need for 'inline' replacement */ +/* The code is compiled with C++ compiler. + C++ always supports 'inline'. */ +# define CURL_INLINE inline /* 'inline' keyword supported */ #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901 - /* C99 (and later) supports 'inline' keyword */ - /* No need for 'inline' replacement */ +/* C99 (and later) supports 'inline' keyword */ +# define CURL_INLINE inline /* 'inline' keyword supported */ #elif defined(__GNUC__) && __GNUC__ >= 3 - /* GCC supports '__inline__' as an extension */ -# define inline __inline__ -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - /* MSC supports '__inline' from VS 2005 (or even earlier) */ -# define inline __inline +/* GCC supports '__inline__' as an extension */ +# define CURL_INLINE __inline__ +#elif defined(_MSC_VER) +# define CURL_INLINE __inline #else - /* Probably 'inline' is not supported by compiler. - Define to the empty string to be on the safe side. */ -# define inline /* empty */ +/* Probably 'inline' is not supported by compiler. + Define to the empty string to be on the safe side. */ +# define CURL_INLINE /* empty */ #endif #endif /* HEADER_CURL_SETUP_H */ diff --git a/lib/curl_setup_once.h b/lib/curl_setup_once.h index 1521e69..037bffb 100644 --- a/lib/curl_setup_once.h +++ b/lib/curl_setup_once.h @@ -24,7 +24,6 @@ * ***************************************************************************/ - /* * Inclusion of common header files. */ @@ -40,14 +39,6 @@ #include #endif -#ifdef NEED_MALLOC_H -#include -#endif - -#ifdef NEED_MEMORY_H -#include -#endif - #ifdef HAVE_SYS_STAT_H #include #endif @@ -56,8 +47,11 @@ #include #endif -#ifdef _WIN32 +#ifdef HAVE_IO_H #include +#endif + +#ifdef HAVE_FCNTL_H #include #endif @@ -111,8 +105,8 @@ #ifndef HAVE_STRUCT_TIMEVAL struct timeval { - long tv_sec; - long tv_usec; + long tv_sec; + long tv_usec; }; #endif @@ -195,7 +189,7 @@ struct timeval { # define sclose(x) closesocket((x)) #elif defined(HAVE_CLOSESOCKET_CAMEL) # define sclose(x) CloseSocket((x)) -#elif defined(HAVE_CLOSE_S) +#elif defined(MSDOS) /* Watt-32 */ # define sclose(x) close_s((x)) #elif defined(USE_LWIPSOCK) # define sclose(x) lwip_close((x)) @@ -233,8 +227,8 @@ struct timeval { #ifndef HAVE_BOOL_T typedef enum { - bool_false = 0, - bool_true = 1 + bool_false = 0, + bool_true = 1 } bool; /* @@ -397,7 +391,7 @@ typedef unsigned int bit; #ifdef __VMS #define argv_item_t __char_ptr32 #elif defined(_UNICODE) -#define argv_item_t wchar_t * +#define argv_item_t wchar_t * #else #define argv_item_t char * #endif diff --git a/lib/curl_sha256.h b/lib/curl_sha256.h index 00e5b74..f532939 100644 --- a/lib/curl_sha256.h +++ b/lib/curl_sha256.h @@ -26,7 +26,7 @@ ***************************************************************************/ #if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ - || defined(USE_LIBSSH2) + || defined(USE_LIBSSH2) || defined(USE_SSL) #include #include "curl_hmac.h" diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 80fd8cf..46c0bd5 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -283,28 +283,26 @@ Curl_sha512_256_finish(unsigned char *digest, #ifdef __GNUC__ # if defined(__has_attribute) && defined(__STDC_VERSION__) # if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901 -# define MHDX_INLINE inline __attribute__((always_inline)) +# define CURL_FORCEINLINE CURL_INLINE __attribute__((always_inline)) # endif # endif #endif -#if !defined(MHDX_INLINE) && \ +#if !defined(CURL_FORCEINLINE) && \ defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__) -# if _MSC_VER >= 1400 -# define MHDX_INLINE __forceinline -# endif +# define CURL_FORCEINLINE __forceinline #endif -#if !defined(MHDX_INLINE) - /* Assume that 'inline' keyword works or the +#if !defined(CURL_FORCEINLINE) + /* Assume that 'CURL_INLINE' keyword works or the * macro was already defined correctly. */ -# define MHDX_INLINE inline +# define CURL_FORCEINLINE CURL_INLINE #endif /* Bits manipulation macros and functions. Can be moved to other headers to reuse. */ -#define MHDX_GET_64BIT_BE(ptr) \ +#define CURL_GET_64BIT_BE(ptr) \ ( ((curl_uint64_t)(((const unsigned char*)(ptr))[0]) << 56) | \ ((curl_uint64_t)(((const unsigned char*)(ptr))[1]) << 48) | \ ((curl_uint64_t)(((const unsigned char*)(ptr))[2]) << 40) | \ @@ -314,7 +312,7 @@ Curl_sha512_256_finish(unsigned char *digest, ((curl_uint64_t)(((const unsigned char*)(ptr))[6]) << 8) | \ (curl_uint64_t)(((const unsigned char*)(ptr))[7]) ) -#define MHDX_PUT_64BIT_BE(ptr,val) do { \ +#define CURL_PUT_64BIT_BE(ptr,val) do { \ ((unsigned char*)(ptr))[7]=(unsigned char)((curl_uint64_t)(val)); \ ((unsigned char*)(ptr))[6]=(unsigned char)(((curl_uint64_t)(val)) >> 8); \ ((unsigned char*)(ptr))[5]=(unsigned char)(((curl_uint64_t)(val)) >> 16); \ @@ -328,8 +326,8 @@ Curl_sha512_256_finish(unsigned char *digest, /* Defined as a function. The macro version may duplicate the binary code * size as each argument is used twice, so if any calculation is used * as an argument, the calculation could be done twice. */ -static MHDX_INLINE curl_uint64_t -MHDx_rotr64(curl_uint64_t value, unsigned int bits) +static CURL_FORCEINLINE curl_uint64_t +Curl_rotr64(curl_uint64_t value, unsigned int bits) { bits %= 64; if(0 == bits) @@ -388,7 +386,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits) /** * SHA-512/256 calculation context */ -struct mhdx_sha512_256ctx +struct Curl_sha512_256ctx { /** * Intermediate hash value. The variable is properly aligned. Smart @@ -416,7 +414,7 @@ struct mhdx_sha512_256ctx /** * Context type used for SHA-512/256 calculations */ -typedef struct mhdx_sha512_256ctx Curl_sha512_256_ctx; +typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx; /** @@ -426,9 +424,9 @@ typedef struct mhdx_sha512_256ctx Curl_sha512_256_ctx; * @return always CURLE_OK */ static CURLcode -MHDx_sha512_256_init(void *context) +Curl_sha512_256_init(void *context) { - struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *) context; + struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context; /* Check whether the header and this file use the same numbers */ DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE); @@ -462,7 +460,7 @@ MHDx_sha512_256_init(void *context) * @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], +Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], const void *data) { /* Working variables, @@ -488,13 +486,13 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], /* Four 'Sigma' macro functions. See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ #define SIG0(x) \ - ( MHDx_rotr64((x), 28) ^ MHDx_rotr64((x), 34) ^ MHDx_rotr64((x), 39) ) + ( Curl_rotr64((x), 28) ^ Curl_rotr64((x), 34) ^ Curl_rotr64((x), 39) ) #define SIG1(x) \ - ( MHDx_rotr64((x), 14) ^ MHDx_rotr64((x), 18) ^ MHDx_rotr64((x), 41) ) + ( Curl_rotr64((x), 14) ^ Curl_rotr64((x), 18) ^ Curl_rotr64((x), 41) ) #define sig0(x) \ - ( MHDx_rotr64((x), 1) ^ MHDx_rotr64((x), 8) ^ ((x) >> 7) ) + ( Curl_rotr64((x), 1) ^ Curl_rotr64((x), 8) ^ ((x) >> 7) ) #define sig1(x) \ - ( MHDx_rotr64((x), 19) ^ MHDx_rotr64((x), 61) ^ ((x) >> 6) ) + ( Curl_rotr64((x), 19) ^ Curl_rotr64((x), 61) ^ ((x) >> 6) ) if(1) { unsigned int t; @@ -577,7 +575,7 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], Input data must be read in big-endian bytes order, see FIPS PUB 180-4 section 3.1.2. */ #define SHA512_GET_W_FROM_DATA(buf,t) \ - MHDX_GET_64BIT_BE( \ + CURL_GET_64BIT_BE( \ ((const unsigned char*) (buf)) + (t) * SHA512_256_BYTES_IN_WORD) /* During first 16 steps, before making any calculation on each step, the @@ -628,12 +626,12 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], * @return always CURLE_OK */ static CURLcode -MHDx_sha512_256_update(void *context, +Curl_sha512_256_update(void *context, const unsigned char *data, size_t length) { unsigned int bytes_have; /**< Number of bytes in the context buffer */ - struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context; + struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context; /* the void pointer here is required to mute Intel compiler warning */ void *const ctx_buf = ctx->buffer; @@ -661,7 +659,7 @@ MHDx_sha512_256_update(void *context, bytes_left); data += bytes_left; length -= bytes_left; - MHDx_sha512_256_transform(ctx->H, ctx->buffer); + Curl_sha512_256_transform(ctx->H, ctx->buffer); bytes_have = 0; } } @@ -669,7 +667,7 @@ MHDx_sha512_256_update(void *context, 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); + Curl_sha512_256_transform(ctx->H, data); data += CURL_SHA512_256_BLOCK_SIZE; length -= CURL_SHA512_256_BLOCK_SIZE; } @@ -705,10 +703,10 @@ MHDx_sha512_256_update(void *context, * @return always CURLE_OK */ static CURLcode -MHDx_sha512_256_finish(unsigned char *digest, +Curl_sha512_256_finish(unsigned char *digest, void *context) { - struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context; + struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context; curl_uint64_t num_bits; /**< Number of processed bits */ unsigned int bytes_have; /**< Number of bytes in the context buffer */ /* the void pointer here is required to mute Intel compiler warning */ @@ -742,7 +740,7 @@ MHDx_sha512_256_finish(unsigned char *digest, memset(((unsigned char *) ctx_buf) + bytes_have, 0, CURL_SHA512_256_BLOCK_SIZE - bytes_have); /* Process the full block. */ - MHDx_sha512_256_transform(ctx->H, ctx->buffer); + Curl_sha512_256_transform(ctx->H, ctx->buffer); /* Start the new block. */ bytes_have = 0; } @@ -754,37 +752,32 @@ MHDx_sha512_256_finish(unsigned char *digest, 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) \ + CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ + CURL_SHA512_256_BLOCK_SIZE \ - SHA512_256_SIZE_OF_LEN_ADD, \ ctx->count_bits_hi); - MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ + CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ + CURL_SHA512_256_BLOCK_SIZE \ - SHA512_256_SIZE_OF_LEN_ADD \ + SHA512_256_BYTES_IN_WORD, \ num_bits); /* Process the full final block. */ - MHDx_sha512_256_transform(ctx->H, ctx->buffer); + Curl_sha512_256_transform(ctx->H, ctx->buffer); /* Put in BE mode the leftmost part of the hash as the final digest. See FIPS PUB 180-4 section 6.7. */ - MHDX_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]); - MHDX_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]); - MHDX_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]); - MHDX_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]); + CURL_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]); + CURL_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]); + CURL_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]); + CURL_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]); /* Erase potentially sensitive data. */ - memset(ctx, 0, sizeof(struct mhdx_sha512_256ctx)); + memset(ctx, 0, sizeof(struct Curl_sha512_256ctx)); return CURLE_OK; } -/* Map to the local implementation */ -#define Curl_sha512_256_init MHDx_sha512_256_init -#define Curl_sha512_256_update MHDx_sha512_256_update -#define Curl_sha512_256_finish MHDx_sha512_256_finish - #endif /* Local SHA-512/256 code */ diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 535a1ff..ac39afa 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -81,27 +81,27 @@ extern PSecurityFunctionTable Curl_pSecFn; #endif #ifndef SEC_I_SIGNATURE_NEEDED -# define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) +#define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) #endif #ifndef CRYPT_E_REVOKED -# define CRYPT_E_REVOKED ((HRESULT)0x80092010L) +#define CRYPT_E_REVOKED ((HRESULT)0x80092010L) #endif #ifndef CRYPT_E_NO_REVOCATION_DLL -# define CRYPT_E_NO_REVOCATION_DLL ((HRESULT)0x80092011L) +#define CRYPT_E_NO_REVOCATION_DLL ((HRESULT)0x80092011L) #endif #ifndef CRYPT_E_NO_REVOCATION_CHECK -# define CRYPT_E_NO_REVOCATION_CHECK ((HRESULT)0x80092012L) +#define CRYPT_E_NO_REVOCATION_CHECK ((HRESULT)0x80092012L) #endif #ifndef CRYPT_E_REVOCATION_OFFLINE -# define CRYPT_E_REVOCATION_OFFLINE ((HRESULT)0x80092013L) +#define CRYPT_E_REVOCATION_OFFLINE ((HRESULT)0x80092013L) #endif #ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE -# define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L) +#define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L) #endif #ifdef UNICODE diff --git a/lib/curl_trc.c b/lib/curl_trc.c index a3a107a..e773e4f 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -239,6 +239,24 @@ void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...) } #endif /* !CURL_DISABLE_SMTP */ +#ifdef USE_SSL +struct curl_trc_feat Curl_trc_feat_ssls = { + "SSLS", + CURL_LOG_LVL_NONE, +}; + +void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_ssls, fmt, ap); + va_end(ap); + } +} +#endif /* USE_SSL */ + #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) struct curl_trc_feat Curl_trc_feat_ws = { "WS", @@ -279,6 +297,9 @@ static struct trc_feat_def trc_feats[] = { #ifndef CURL_DISABLE_SMTP { &Curl_trc_feat_smtp, TRC_CT_PROTOCOL }, #endif +#ifdef USE_SSL + { &Curl_trc_feat_ssls, TRC_CT_NETWORK }, +#endif #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) { &Curl_trc_feat_ws, TRC_CT_PROTOCOL }, #endif @@ -319,7 +340,7 @@ static struct trc_cft_def trc_cfts[] = { #ifdef USE_HTTP3 { &Curl_cft_http3, TRC_CT_PROTOCOL }, #endif -#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) +#if !defined(CURL_DISABLE_HTTP) { &Curl_cft_http_connect, TRC_CT_PROTOCOL }, #endif }; @@ -427,4 +448,53 @@ CURLcode Curl_trc_init(void) return CURLE_OK; } +void Curl_infof(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +void Curl_trc_cf_infof(struct Curl_easy *data, + struct Curl_cfilter *cf, + const char *fmt, ...) +{ + (void)data; (void)cf; (void)fmt; +} + +struct curl_trc_feat; + +void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +#ifndef CURL_DISABLE_FTP +void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} +#endif +#ifndef CURL_DISABLE_SMTP +void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} +#endif +#if !defined(CURL_DISABLE_WEBSOCKETS) || !defined(CURL_DISABLE_HTTP) +void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} +#endif + +void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; + (void)fmt; +} + #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ diff --git a/lib/curl_trc.h b/lib/curl_trc.h index 67a7f4a..9b4e36e 100644 --- a/lib/curl_trc.h +++ b/lib/curl_trc.h @@ -70,7 +70,45 @@ void Curl_failf(struct Curl_easy *data, #define CURL_HAVE_C99 #endif -#ifdef CURL_HAVE_C99 +/** + * Output an informational message when transfer's verbose logging is enabled. + */ +void Curl_infof(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); + +/** + * Output an informational message when both transfer's verbose logging + * and connection filters verbose logging are enabled. + */ +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, + const char *fmt, ...) CURL_PRINTF(3, 4); +void Curl_trc_write(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +void Curl_trc_read(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); + +#ifndef CURL_DISABLE_FTP +extern struct curl_trc_feat Curl_trc_feat_ftp; +void Curl_trc_ftp(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif +#ifndef CURL_DISABLE_SMTP +extern struct curl_trc_feat Curl_trc_feat_smtp; +void Curl_trc_smtp(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif +#ifdef USE_SSL +extern struct curl_trc_feat Curl_trc_feat_ssls; +void Curl_trc_ssls(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif +#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +extern struct curl_trc_feat Curl_trc_feat_ws; +void Curl_trc_ws(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif + +#if defined(CURL_HAVE_C99) && !defined(CURL_DISABLE_VERBOSE_STRINGS) #define infof(data, ...) \ do { if(Curl_trc_is_verbose(data)) \ Curl_infof(data, __VA_ARGS__); } while(0) @@ -94,6 +132,11 @@ void Curl_failf(struct Curl_easy *data, do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) \ Curl_trc_smtp(data, __VA_ARGS__); } while(0) #endif /* !CURL_DISABLE_SMTP */ +#ifdef USE_SSL +#define CURL_TRC_SSLS(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) \ + Curl_trc_ssls(data, __VA_ARGS__); } while(0) +#endif /* USE_SSL */ #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) #define CURL_TRC_WS(data, ...) \ do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) \ @@ -113,6 +156,9 @@ void Curl_failf(struct Curl_easy *data, #ifndef CURL_DISABLE_SMTP #define CURL_TRC_SMTP Curl_trc_smtp #endif +#ifdef USE_SSL +#define CURL_TRC_SSLS Curl_trc_ssls +#endif #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) #define CURL_TRC_WS Curl_trc_ws #endif @@ -140,40 +186,6 @@ extern struct curl_trc_feat Curl_trc_feat_write; (Curl_trc_is_verbose(data) && \ (ft)->log_level >= CURL_LOG_LVL_INFO) -/** - * Output an informational message when transfer's verbose logging is enabled. - */ -void Curl_infof(struct Curl_easy *data, - const char *fmt, ...) CURL_PRINTF(2, 3); - -/** - * Output an informational message when both transfer's verbose logging - * and connection filters verbose logging are enabled. - */ -void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, - const char *fmt, ...) CURL_PRINTF(3, 4); -void Curl_trc_write(struct Curl_easy *data, - const char *fmt, ...) CURL_PRINTF(2, 3); -void Curl_trc_read(struct Curl_easy *data, - const char *fmt, ...) CURL_PRINTF(2, 3); - -#ifndef CURL_DISABLE_FTP -extern struct curl_trc_feat Curl_trc_feat_ftp; -void Curl_trc_ftp(struct Curl_easy *data, - const char *fmt, ...) CURL_PRINTF(2, 3); -#endif -#ifndef CURL_DISABLE_SMTP -extern struct curl_trc_feat Curl_trc_feat_smtp; -void Curl_trc_smtp(struct Curl_easy *data, - const char *fmt, ...) CURL_PRINTF(2, 3); -#endif -#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) -extern struct curl_trc_feat Curl_trc_feat_ws; -void Curl_trc_ws(struct Curl_easy *data, - const char *fmt, ...) CURL_PRINTF(2, 3); -#endif - - #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ /* All informational messages are not compiled in for size savings */ @@ -181,49 +193,6 @@ void Curl_trc_ws(struct Curl_easy *data, #define Curl_trc_cf_is_verbose(x,y) (FALSE) #define Curl_trc_ft_is_verbose(x,y) (FALSE) -static void Curl_infof(struct Curl_easy *data, const char *fmt, ...) -{ - (void)data; (void)fmt; -} - -static void Curl_trc_cf_infof(struct Curl_easy *data, - struct Curl_cfilter *cf, - const char *fmt, ...) -{ - (void)data; (void)cf; (void)fmt; -} - -struct curl_trc_feat; - -static void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) -{ - (void)data; (void)fmt; -} - -static void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) -{ - (void)data; (void)fmt; -} - -#ifndef CURL_DISABLE_FTP -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 -#if !defined(CURL_DISABLE_WEBSOCKETS) || !defined(CURL_DISABLE_HTTP) -static void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...) -{ - (void)data; (void)fmt; -} -#endif - #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ #endif /* HEADER_CURL_TRC_H */ diff --git a/lib/dict.c b/lib/dict.c index 7d9c18d..143b76f 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -93,6 +93,7 @@ const struct Curl_handler Curl_handler_dict = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_DICT, /* defport */ CURLPROTO_DICT, /* protocol */ CURLPROTO_DICT, /* family */ diff --git a/lib/doh.c b/lib/doh.c index 8769372..617a761 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -410,12 +410,6 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, 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 - char *qname = NULL; -# endif -#endif *waitp = FALSE; (void)hostname; (void)port; @@ -462,34 +456,23 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, #endif #ifdef USE_HTTPSRR - /* - * TODO: Figure out the conditions under which we want to make - * a request for an HTTPS RR when we are not doing ECH. For now, - * making this request breaks a bunch of DoH tests, e.g. test2100, - * where the additional request does not match the pre-cooked data - * files, so there is a bit of work attached to making the request - * in a non-ECH use-case. For the present, we will only make the - * request when ECH is enabled in the build and is being used for - * the curl operation. - */ -# ifdef USE_ECH - if(data->set.tls_ech & CURLECH_ENABLE - || data->set.tls_ech & CURLECH_HARD) { - if(port == 443) - qname = strdup(hostname); - else + if(conn->handler->protocol & PROTO_FAMILY_HTTP) { + /* Only use HTTPS RR for HTTP(S) transfers */ + char *qname = NULL; + if(port != PORT_HTTPS) { qname = aprintf("_%d._https.%s", port, hostname); - if(!qname) - goto error; + if(!qname) + goto error; + } result = doh_run_probe(data, &dohp->probe[DOH_SLOT_HTTPS_RR], - DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH], + DNS_TYPE_HTTPS, + qname ? qname : hostname, data->set.str[STRING_DOH], data->multi, dohp->req_hds); - Curl_safefree(qname); + free(qname); if(result) goto error; dohp->pending++; } -# endif #endif *waitp = TRUE; /* this never returns synchronously */ return NULL; @@ -961,11 +944,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, addr = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); -#ifdef __MINGW32__ - addr->sin_family = (short)addrtype; -#else - addr->sin_family = addrtype; -#endif + addr->sin_family = (CURL_SA_FAMILY_T)addrtype; addr->sin_port = htons((unsigned short)port); break; @@ -974,11 +953,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, addr6 = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); -#ifdef __MINGW32__ - addr6->sin6_family = (short)addrtype; -#else - addr6->sin6_family = addrtype; -#endif + addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype; addr6->sin6_port = htons((unsigned short)port); break; #endif @@ -1089,62 +1064,6 @@ static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining, return CURLE_OK; } -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 - * encoding is catenated list of strings each preceded by a one - * octet length - * output is comma-sep list of the strings - * implementations may or may not handle quoting of comma within - * string values, so we might see a comma within the wire format - * version of a string, in which case we will precede that by a - * backslash - same goes for a backslash character, and of course - * we need to use two backslashes in strings when we mean one;-) - */ - int remaining = (int) len; - char *oval; - size_t i; - unsigned char *cp = rrval; - struct dynbuf dval; - - if(!alpns) - return CURLE_OUT_OF_MEMORY; - Curl_dyn_init(&dval, DYN_DOH_RESPONSE); - remaining = (int)len; - cp = rrval; - while(remaining > 0) { - size_t tlen = (size_t) *cp++; - - /* if not 1st time, add comma */ - if(remaining != (int)len && Curl_dyn_addn(&dval, ",", 1)) - goto err; - remaining--; - if(tlen > (size_t)remaining) - goto err; - /* add escape char if needed, clunky but easier to read */ - for(i = 0; i != tlen; i++) { - if('\\' == *cp || ',' == *cp) { - if(Curl_dyn_addn(&dval, "\\", 1)) - goto err; - } - if(Curl_dyn_addn(&dval, cp++, 1)) - goto err; - } - remaining -= (int)tlen; - } - /* this string is always null terminated */ - oval = Curl_dyn_ptr(&dval); - if(!oval) - goto err; - *alpns = oval; - return CURLE_OK; -err: - Curl_dyn_free(&dval); - return CURLE_BAD_CONTENT_ENCODING; -} - #ifdef DEBUGBUILD static CURLcode doh_test_alpn_escapes(void) { @@ -1156,24 +1075,20 @@ static CURLcode doh_test_alpn_escapes(void) 0x68, 0x32 /* value "h2" */ }; size_t example_len = sizeof(example); - char *aval = NULL; - static const char *expected = "f\\\\oo\\,bar,h2"; + unsigned char aval[MAX_HTTPSRR_ALPNS] = { 0 }; + static const char expected[2] = { ALPN_h2, ALPN_none }; - if(doh_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK) + if(Curl_httpsrr_decode_alpn(example, example_len, aval) != CURLE_OK) return CURLE_BAD_CONTENT_ENCODING; - if(strlen(aval) != strlen(expected)) - return CURLE_BAD_CONTENT_ENCODING; - if(memcmp(aval, expected, strlen(aval))) + if(memcmp(aval, expected, sizeof(expected))) return CURLE_BAD_CONTENT_ENCODING; return CURLE_OK; } #endif -static CURLcode doh_resp_decode_httpsrr(unsigned char *rrval, size_t len, +static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len, struct Curl_https_rrinfo **hrr) { - size_t remaining = len; - unsigned char *cp = rrval; uint16_t pcode = 0, plen = 0; struct Curl_https_rrinfo *lhrr = NULL; char *dnsname = NULL; @@ -1183,73 +1098,73 @@ static CURLcode doh_resp_decode_httpsrr(unsigned char *rrval, size_t len, if(doh_test_alpn_escapes() != CURLE_OK) return CURLE_OUT_OF_MEMORY; #endif + if(len <= 2) + return CURLE_BAD_FUNCTION_ARGUMENT; lhrr = calloc(1, sizeof(struct Curl_https_rrinfo)); if(!lhrr) return CURLE_OUT_OF_MEMORY; - lhrr->val = Curl_memdup(rrval, len); - if(!lhrr->val) - goto err; - lhrr->len = len; - if(remaining <= 2) - goto err; - lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]); + lhrr->priority = doh_get16bit(cp, 0); cp += 2; - remaining -= (uint16_t)2; - if(doh_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK) + len -= 2; + if(doh_decode_rdata_name(&cp, &len, &dnsname) != CURLE_OK) goto err; lhrr->target = dnsname; - while(remaining >= 4) { - pcode = (uint16_t)((*cp << 8) + (*(cp + 1))); - cp += 2; - plen = (uint16_t)((*cp << 8) + (*(cp + 1))); - cp += 2; - remaining -= 4; - if(pcode == HTTPS_RR_CODE_ALPN) { - if(doh_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK) + lhrr->port = -1; /* until set */ + while(len >= 4) { + pcode = doh_get16bit(cp, 0); + plen = doh_get16bit(cp, 2); + cp += 4; + len -= 4; + switch(pcode) { + case HTTPS_RR_CODE_ALPN: + if(Curl_httpsrr_decode_alpn(cp, plen, lhrr->alpns) != CURLE_OK) goto err; - } - if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN) + break; + case HTTPS_RR_CODE_NO_DEF_ALPN: lhrr->no_def_alpn = TRUE; - else if(pcode == HTTPS_RR_CODE_IPV4) { + break; + case HTTPS_RR_CODE_IPV4: if(!plen) goto err; lhrr->ipv4hints = Curl_memdup(cp, plen); if(!lhrr->ipv4hints) goto err; lhrr->ipv4hints_len = (size_t)plen; - } - else if(pcode == HTTPS_RR_CODE_ECH) { + break; + case HTTPS_RR_CODE_ECH: if(!plen) goto err; lhrr->echconfiglist = Curl_memdup(cp, plen); if(!lhrr->echconfiglist) goto err; lhrr->echconfiglist_len = (size_t)plen; - } - else if(pcode == HTTPS_RR_CODE_IPV6) { + break; + case HTTPS_RR_CODE_IPV6: if(!plen) goto err; lhrr->ipv6hints = Curl_memdup(cp, plen); if(!lhrr->ipv6hints) goto err; lhrr->ipv6hints_len = (size_t)plen; + break; + case HTTPS_RR_CODE_PORT: + lhrr->port = doh_get16bit(cp, 0); + break; + default: + break; } - if(plen > 0 && plen <= remaining) { + if(plen > 0 && plen <= len) { cp += plen; - remaining -= plen; + len -= plen; } } - DEBUGASSERT(!remaining); + DEBUGASSERT(!len); *hrr = lhrr; return CURLE_OK; err: - if(lhrr) { - Curl_safefree(lhrr->target); - Curl_safefree(lhrr->echconfiglist); - Curl_safefree(lhrr->val); - Curl_safefree(lhrr->alpns); - Curl_safefree(lhrr); - } + Curl_safefree(lhrr->target); + Curl_safefree(lhrr->echconfiglist); + Curl_safefree(lhrr); return CURLE_OUT_OF_MEMORY; } @@ -1260,8 +1175,9 @@ static void doh_print_httpsrr(struct Curl_easy *data, DEBUGASSERT(hrr); infof(data, "HTTPS RR: priority %d, target: %s", hrr->priority, hrr->target); - if(hrr->alpns) - infof(data, "HTTPS RR: alpns %s", hrr->alpns); + if(hrr->alpns[0] != ALPN_none) + infof(data, "HTTPS RR: alpns %u %u %u %u", + hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]); else infof(data, "HTTPS RR: no alpns"); if(hrr->no_def_alpn) diff --git a/lib/doh.h b/lib/doh.h index aae32a6..5364486 100644 --- a/lib/doh.h +++ b/lib/doh.h @@ -28,6 +28,7 @@ #include "curl_addrinfo.h" #ifdef USE_HTTPSRR # include +# include "httpsrr.h" #endif #ifndef CURL_DISABLE_DOH @@ -124,19 +125,6 @@ struct dohaddr { #ifdef USE_HTTPSRR /* - * These are the code points for DNS wire format SvcParams as - * per draft-ietf-dnsop-svcb-https - * Not all are supported now, and even those that are may need - * more work in future to fully support the spec. - */ -#define HTTPS_RR_CODE_ALPN 0x01 -#define HTTPS_RR_CODE_NO_DEF_ALPN 0x02 -#define HTTPS_RR_CODE_PORT 0x03 -#define HTTPS_RR_CODE_IPV4 0x04 -#define HTTPS_RR_CODE_ECH 0x05 -#define HTTPS_RR_CODE_IPV6 0x06 - -/* * These may need escaping when found within an ALPN string * value. */ diff --git a/lib/dynbuf.c b/lib/dynbuf.c index eab07ef..1516426 100644 --- a/lib/dynbuf.c +++ b/lib/dynbuf.c @@ -244,6 +244,18 @@ char *Curl_dyn_ptr(const struct dynbuf *s) return s->bufr; } +char *Curl_dyn_take(struct dynbuf *s, size_t *plen) +{ + char *ptr = s->bufr; + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + *plen = s->leng; + s->bufr = NULL; + s->leng = 0; + s->allc = 0; + return ptr; +} + /* * Returns an unsigned pointer to the buffer. */ diff --git a/lib/dynbuf.h b/lib/dynbuf.h index 7dbaab8..154b54c 100644 --- a/lib/dynbuf.h +++ b/lib/dynbuf.h @@ -39,6 +39,7 @@ #define Curl_dyn_uptr(a) curlx_dyn_uptr(a) #define Curl_dyn_len(a) curlx_dyn_len(a) #define Curl_dyn_reset(a) curlx_dyn_reset(a) +#define Curl_dyn_take(a,b) curlx_dyn_take(a,b) #define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b) #define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b) #define curlx_dynbuf dynbuf /* for the struct name */ @@ -75,6 +76,10 @@ size_t Curl_dyn_len(const struct dynbuf *s); /* The implementation of this function exists in mprintf.c */ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save); +/* Take the buffer out of the dynbuf. Caller has ownership and + * dynbuf resets to initial state. */ +char *Curl_dyn_take(struct dynbuf *s, size_t *plen); + /* Dynamic buffer max sizes */ #define DYN_DOH_RESPONSE 3000 #define DYN_DOH_CNAME 256 diff --git a/lib/easy.c b/lib/easy.c index ac8fab3..1573a9d 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -48,6 +48,7 @@ #include #include "transfer.h" #include "vtls/vtls.h" +#include "vtls/vtls_scache.h" #include "url.h" #include "getinfo.h" #include "hostip.h" @@ -761,12 +762,25 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) return CURLE_FAILED_INIT; } + /* if the handle has a connection still attached (it is/was a connect-only + handle) then disconnect before performing */ + if(data->conn) { + struct connectdata *c; + curl_socket_t s; + Curl_detach_connection(data); + s = Curl_getconnectinfo(data, &c); + if((s != CURL_SOCKET_BAD) && c) { + Curl_cpool_disconnect(data, c, TRUE); + } + DEBUGASSERT(!data->conn); + } + if(data->multi_easy) multi = data->multi_easy; else { - /* this multi handle will only ever have a single easy handled attached - to it, so make it use minimal hashes */ - multi = Curl_multi_handle(1, 3, 7); + /* this multi handle will only ever have a single easy handle attached to + it, so make it use minimal hash sizes */ + multi = Curl_multi_handle(1, 3, 7, 3); if(!multi) return CURLE_OUT_OF_MEMORY; } @@ -1336,3 +1350,41 @@ CURLcode curl_easy_upkeep(CURL *d) /* Use the common function to keep connections alive. */ return Curl_cpool_upkeep(data); } + +CURLcode curl_easy_ssls_import(CURL *d, const char *session_key, + const unsigned char *shmac, size_t shmac_len, + const unsigned char *sdata, size_t sdata_len) +{ +#ifdef USE_SSLS_EXPORT + struct Curl_easy *data = d; + if(!GOOD_EASY_HANDLE(data)) + return CURLE_BAD_FUNCTION_ARGUMENT; + return Curl_ssl_session_import(data, session_key, + shmac, shmac_len, sdata, sdata_len); +#else + (void)d; + (void)session_key; + (void)shmac; + (void)shmac_len; + (void)sdata; + (void)sdata_len; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode curl_easy_ssls_export(CURL *d, + curl_ssls_export_cb *export_fn, + void *userptr) +{ +#ifdef USE_SSLS_EXPORT + struct Curl_easy *data = d; + if(!GOOD_EASY_HANDLE(data)) + return CURLE_BAD_FUNCTION_ARGUMENT; + return Curl_ssl_session_export(data, export_fn, userptr); +#else + (void)d; + (void)export_fn; + (void)userptr; + return CURLE_NOT_BUILT_IN; +#endif +} diff --git a/lib/easy_lock.h b/lib/easy_lock.h index 4f6764d..ec324cf 100644 --- a/lib/easy_lock.h +++ b/lib/easy_lock.h @@ -69,7 +69,7 @@ #endif -static inline void curl_simple_lock_lock(curl_simple_lock *lock) +static CURL_INLINE void curl_simple_lock_lock(curl_simple_lock *lock) { for(;;) { if(!atomic_exchange_explicit(lock, true, memory_order_acquire)) @@ -81,6 +81,8 @@ static inline void curl_simple_lock_lock(curl_simple_lock *lock) __builtin_ia32_pause(); #elif defined(__aarch64__) __asm__ volatile("yield" ::: "memory"); +#elif defined(_WIN32) + Sleep(1); #elif defined(HAVE_SCHED_YIELD) sched_yield(); #endif @@ -88,7 +90,7 @@ static inline void curl_simple_lock_lock(curl_simple_lock *lock) } } -static inline void curl_simple_lock_unlock(curl_simple_lock *lock) +static CURL_INLINE void curl_simple_lock_unlock(curl_simple_lock *lock) { atomic_store_explicit(lock, false, memory_order_release); } diff --git a/lib/easyoptions.c b/lib/easyoptions.c index 81091c4..f2ced2c 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -377,6 +377,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (326 + 1)); + return (CURLOPT_LASTENTRY % 10000) != (326 + 1); } #endif diff --git a/lib/file.c b/lib/file.c index 4cd8d0f..7751acf 100644 --- a/lib/file.c +++ b/lib/file.c @@ -78,18 +78,12 @@ #include "curl_memory.h" #include "memdebug.h" -#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__) +#if defined(_WIN32) || defined(MSDOS) #define DOS_FILESYSTEM 1 #elif defined(__amigaos4__) #define AMIGA_FILESYSTEM 1 #endif -#ifdef OPEN_NEEDS_ARG3 -# define open_readonly(p,f) open((p),(f),(0)) -#else -# define open_readonly(p,f) open((p),(f)) -#endif - /* * Forward declarations. */ @@ -126,6 +120,7 @@ const struct Curl_handler Curl_handler_file = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ 0, /* defport */ CURLPROTO_FILE, /* protocol */ CURLPROTO_FILE, /* family */ @@ -209,7 +204,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) return CURLE_URL_MALFORMAT; } - fd = open_readonly(actual_path, O_RDONLY|O_BINARY); + fd = open(actual_path, O_RDONLY|CURL_O_BINARY); file->path = actual_path; #else if(memchr(real_path, 0, real_path_len)) { @@ -233,16 +228,16 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) extern int __unix_path_semantics; if(strchr(real_path + 1, ':')) { /* Amiga absolute path */ - fd = open_readonly(real_path + 1, O_RDONLY); + fd = open(real_path + 1, O_RDONLY); file->path++; } else if(__unix_path_semantics) { /* -lunix fallback */ - fd = open_readonly(real_path, O_RDONLY); + fd = open(real_path, O_RDONLY); } } #else - fd = open_readonly(real_path, O_RDONLY); + fd = open(real_path, O_RDONLY); file->path = real_path; #endif #endif @@ -318,18 +313,18 @@ static CURLcode file_upload(struct Curl_easy *data) if(!dir[1]) return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ -#ifdef O_BINARY -#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY -#else -#define MODE_DEFAULT O_WRONLY|O_CREAT -#endif - + mode = O_WRONLY|O_CREAT|CURL_O_BINARY; if(data->state.resume_from) - mode = MODE_DEFAULT|O_APPEND; + mode |= O_APPEND; else - mode = MODE_DEFAULT|O_TRUNC; + mode |= O_TRUNC; +#if (defined(ANDROID) || defined(__ANDROID__)) && \ + (defined(__i386__) || defined(__arm__)) + fd = open(file->path, mode, (mode_t)data->set.new_file_perms); +#else fd = open(file->path, mode, data->set.new_file_perms); +#endif if(fd < 0) { failf(data, "cannot open %s for writing", file->path); return CURLE_WRITE_ERROR; @@ -553,8 +548,13 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(data->state.resume_from) { if(!S_ISDIR(statbuf.st_mode)) { +#ifdef __AMIGA__ + if(data->state.resume_from != + lseek(fd, (off_t)data->state.resume_from, SEEK_SET)) +#else if(data->state.resume_from != lseek(fd, data->state.resume_from, SEEK_SET)) +#endif return CURLE_BAD_DOWNLOAD_RESUME; } else { diff --git a/lib/fopen.c b/lib/fopen.c index 7373e08..90dc0ae 100644 --- a/lib/fopen.c +++ b/lib/fopen.c @@ -53,7 +53,7 @@ #ifdef _WIN32 #define PATHSEP "\\" #define IS_SEP(x) (((x) == '/') || ((x) == '\\')) -#elif defined(MSDOS) || defined(__EMX__) || defined(OS2) +#elif defined(MSDOS) || defined(OS2) #define PATHSEP "\\" #define IS_SEP(x) ((x) == '\\') #else diff --git a/lib/ftp.c b/lib/ftp.c index 16ab0af..cb0fada 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -250,6 +250,7 @@ const struct Curl_handler Curl_handler_ftp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_FTP, /* defport */ CURLPROTO_FTP, /* protocol */ CURLPROTO_FTP, /* family */ @@ -282,6 +283,7 @@ const struct Curl_handler Curl_handler_ftps = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_FTPS, /* defport */ CURLPROTO_FTPS, /* protocol */ CURLPROTO_FTP, /* family */ @@ -2079,10 +2081,19 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, /* If we asked for a time of the file and we actually got one as well, we "emulate" an HTTP-style header in our output. */ +#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) +#pragma GCC diagnostic push +/* 'time_t' is unsigned in MSDOS and AmigaOS. Silence: + warning: comparison of unsigned expression in '>= 0' is always true */ +#pragma GCC diagnostic ignored "-Wtype-limits" +#endif if(data->req.no_body && ftpc->file && data->set.get_filetime && (data->info.filetime >= 0) ) { +#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) +#pragma GCC diagnostic pop +#endif char headerbuf[128]; int headerbuflen; time_t filetime = data->info.filetime; @@ -3154,7 +3165,7 @@ static CURLcode ftp_connect(struct Curl_easy *data, PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp); - if(conn->handler->flags & PROTOPT_SSL) { + if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* BLOCKING */ result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); if(result) @@ -4097,8 +4108,8 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, } #ifdef _MSC_VER -/* warning C4706: assignment within conditional expression */ -#pragma warning(disable:4706) +#pragma warning(push) +#pragma warning(disable:4706) /* assignment within conditional expression */ #endif /*********************************************************************** @@ -4244,7 +4255,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) else n -= ftpc->file ? strlen(ftpc->file) : 0; - if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) { + if((strlen(oldPath) == n) && rawPath && !strncmp(rawPath, oldPath, n)) { infof(data, "Request has same path as previous transfer"); ftpc->cwddone = TRUE; } @@ -4255,6 +4266,10 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) return CURLE_OK; } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + /* call this when the DO phase has completed */ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected) { diff --git a/lib/functypes.h b/lib/functypes.h index ea66d32..b4dccc0 100644 --- a/lib/functypes.h +++ b/lib/functypes.h @@ -62,6 +62,7 @@ /* int send(int, const char *, int, int); */ #define SEND_TYPE_ARG1 int +#define SEND_QUAL_ARG2 #define SEND_TYPE_ARG2 char * #define SEND_TYPE_ARG3 int #define SEND_TYPE_RETV int diff --git a/lib/getinfo.c b/lib/getinfo.c index 9144ad7..ae6b3b8 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -69,6 +69,8 @@ CURLcode Curl_initinfo(struct Curl_easy *data) info->request_size = 0; info->proxyauthavail = 0; info->httpauthavail = 0; + info->proxyauthpicked = 0; + info->httpauthpicked = 0; info->numconnects = 0; free(info->contenttype); @@ -238,8 +240,10 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, case CURLINFO_FILETIME: if(data->info.filetime > LONG_MAX) *param_longp = LONG_MAX; +#if !defined(MSDOS) && !defined(__AMIGA__) else if(data->info.filetime < LONG_MIN) *param_longp = LONG_MIN; +#endif else *param_longp = (long)data->info.filetime; break; @@ -270,6 +274,14 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, lptr.to_long = param_longp; *lptr.to_ulong = data->info.proxyauthavail; break; + case CURLINFO_HTTPAUTH_USED: + lptr.to_long = param_longp; + *lptr.to_ulong = data->info.httpauthpicked; + break; + case CURLINFO_PROXYAUTH_USED: + lptr.to_long = param_longp; + *lptr.to_ulong = data->info.proxyauthpicked; + break; case CURLINFO_OS_ERRNO: *param_longp = data->state.os_errno; break; @@ -377,6 +389,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, case CURLINFO_APPCONNECT_TIME_T: case CURLINFO_PRETRANSFER_TIME_T: case CURLINFO_POSTTRANSFER_TIME_T: + case CURLINFO_QUEUE_TIME_T: case CURLINFO_STARTTRANSFER_TIME_T: case CURLINFO_REDIRECT_TIME_T: case CURLINFO_SPEED_DOWNLOAD_T: diff --git a/lib/gopher.c b/lib/gopher.c index 051e6e7..00d42f0 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -79,6 +79,7 @@ const struct Curl_handler Curl_handler_gopher = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_GOPHER, /* defport */ CURLPROTO_GOPHER, /* protocol */ CURLPROTO_GOPHER, /* family */ @@ -104,6 +105,7 @@ const struct Curl_handler Curl_handler_gophers = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_GOPHER, /* defport */ CURLPROTO_GOPHERS, /* protocol */ CURLPROTO_GOPHER, /* family */ diff --git a/lib/hash.c b/lib/hash.c index 1910ac5..aa9904e 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -42,6 +42,8 @@ hash_element_dtor(void *user, void *element) { struct Curl_hash *h = (struct Curl_hash *) user; struct Curl_hash_element *e = (struct Curl_hash_element *) element; + DEBUGASSERT(h); + DEBUGASSERT(e); if(e->ptr) { if(e->dtor) diff --git a/lib/hmac.c b/lib/hmac.c index 088c9bd..7b8a29e 100644 --- a/lib/hmac.c +++ b/lib/hmac.c @@ -26,8 +26,9 @@ #include "curl_setup.h" -#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ - || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ + !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ + defined(USE_SSL) #include diff --git a/lib/hostip.c b/lib/hostip.c index 1a9432d..5ab854d 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -198,7 +198,7 @@ hostcache_entry_is_stale(void *datap, void *hc) if(dns->timestamp) { /* age in seconds */ time_t age = prune->now - dns->timestamp; - if(age >= prune->max_age_sec) + if(age >= (time_t)prune->max_age_sec) return TRUE; if(age > prune->oldest) prune->oldest = age; @@ -541,7 +541,9 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name) sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(port16); sa6.sin6_flowinfo = 0; +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID sa6.sin6_scope_id = 0; +#endif (void)Curl_inet_pton(AF_INET6, "::1", ipv6); memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6)); @@ -630,7 +632,7 @@ bool Curl_ipv6works(struct Curl_easy *data) ipv6_works = 1; sclose(s); } - return (ipv6_works > 0); + return ipv6_works > 0; } } #endif /* USE_IPV6 */ @@ -1077,18 +1079,10 @@ static void hostcache_unlink_entry(void *entry) Curl_freeaddrinfo(dns->addr); #ifdef USE_HTTPSRR if(dns->hinfo) { - if(dns->hinfo->target) - free(dns->hinfo->target); - if(dns->hinfo->alpns) - free(dns->hinfo->alpns); - if(dns->hinfo->ipv4hints) - free(dns->hinfo->ipv4hints); - if(dns->hinfo->echconfiglist) - free(dns->hinfo->echconfiglist); - if(dns->hinfo->ipv6hints) - free(dns->hinfo->ipv6hints); - if(dns->hinfo->val) - free(dns->hinfo->val); + free(dns->hinfo->target); + free(dns->hinfo->ipv4hints); + free(dns->hinfo->echconfiglist); + free(dns->hinfo->ipv6hints); free(dns->hinfo); } #endif diff --git a/lib/hostip.h b/lib/hostip.h index b1c5ecb..10f70b2 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -29,6 +29,7 @@ #include "curl_addrinfo.h" #include "timeval.h" /* for timediff_t */ #include "asyn.h" +#include "httpsrr.h" #include @@ -53,6 +54,13 @@ struct hostent; struct Curl_easy; struct connectdata; +enum alpnid { + ALPN_none = 0, + ALPN_h1 = CURLALTSVC_H1, + ALPN_h2 = CURLALTSVC_H2, + ALPN_h3 = CURLALTSVC_H3 +}; + /* * Curl_global_host_cache_init() initializes and sets up a global DNS cache. * Global DNS cache is general badness. Do not use. This will be removed in @@ -62,36 +70,6 @@ struct connectdata; */ struct Curl_hash *Curl_global_host_cache_init(void); -#ifdef USE_HTTPSRR - -#define CURL_MAXLEN_host_name 253 - -struct Curl_https_rrinfo { - size_t len; /* raw encoded length */ - unsigned char *val; /* raw encoded octets */ - /* - * fields from HTTPS RR, with the mandatory fields - * first (priority, target), then the others in the - * order of the keytag numbers defined at - * https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2 - */ - uint16_t priority; - char *target; - char *alpns; /* keytag = 1 */ - bool no_def_alpn; /* keytag = 2 */ - /* - * we do not support ports (keytag = 3) as we do not support - * port-switching yet - */ - unsigned char *ipv4hints; /* keytag = 4 */ - size_t ipv4hints_len; - unsigned char *echconfiglist; /* keytag = 5 */ - size_t echconfiglist_len; - unsigned char *ipv6hints; /* keytag = 6 */ - size_t ipv6hints_len; -}; -#endif - struct Curl_dns_entry { struct Curl_addrinfo *addr; #ifdef USE_HTTPSRR diff --git a/lib/hsts.c b/lib/hsts.c index 5b01372..99452b6 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -41,6 +41,7 @@ #include "rename.h" #include "share.h" #include "strdup.h" +#include "strparse.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -48,10 +49,8 @@ #include "memdebug.h" #define MAX_HSTS_LINE 4095 -#define MAX_HSTS_HOSTLEN 256 -#define MAX_HSTS_HOSTLENSTR "256" -#define MAX_HSTS_DATELEN 64 -#define MAX_HSTS_DATELENSTR "64" +#define MAX_HSTS_HOSTLEN 2048 +#define MAX_HSTS_DATELEN 256 #define UNLIMITED "unlimited" #if defined(DEBUGBUILD) || defined(UNITTESTS) @@ -109,14 +108,13 @@ void Curl_hsts_cleanup(struct hsts **hp) static CURLcode hsts_create(struct hsts *h, const char *hostname, + size_t hlen, bool subdomains, curl_off_t expires) { - size_t hlen; DEBUGASSERT(h); DEBUGASSERT(hostname); - hlen = strlen(hostname); if(hlen && (hostname[hlen - 1] == '.')) /* strip off any trailing dot */ --hlen; @@ -150,6 +148,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, bool subdomains = FALSE; struct stsentry *sts; time_t now = time(NULL); + size_t hlen = strlen(hostname); if(Curl_host_is_ipnum(hostname)) /* "explicit IP address identification of all forms is excluded." @@ -218,7 +217,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, if(!expires) { /* remove the entry if present verbatim (without subdomain match) */ - sts = Curl_hsts(h, hostname, FALSE); + sts = Curl_hsts(h, hostname, hlen, FALSE); if(sts) { Curl_node_remove(&sts->node); hsts_free(sts); @@ -233,14 +232,14 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, expires += now; /* check if it already exists */ - sts = Curl_hsts(h, hostname, FALSE); + sts = Curl_hsts(h, hostname, hlen, FALSE); if(sts) { /* just update these fields */ sts->expires = expires; sts->includeSubDomains = subdomains; } else - return hsts_create(h, hostname, subdomains, expires); + return hsts_create(h, hostname, hlen, subdomains, expires); return CURLE_OK; } @@ -252,12 +251,11 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, * attempted. */ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - bool subdomain) + size_t hlen, bool subdomain) { struct stsentry *bestsub = NULL; if(h) { time_t now = time(NULL); - size_t hlen = strlen(hostname); struct Curl_llist_node *e; struct Curl_llist_node *n; size_t blen = 0; @@ -424,29 +422,40 @@ static CURLcode hsts_add(struct hsts *h, char *line) example.com "20191231 10:00:00" .example.net "20191231 10:00:00" */ - char host[MAX_HSTS_HOSTLEN + 1]; - char date[MAX_HSTS_DATELEN + 1]; - int rc; - - rc = sscanf(line, - "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"", - host, date); - if(2 == rc) { - time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) : - TIME_T_MAX; + struct Curl_str host; + struct Curl_str date; + + if(Curl_str_word(&line, &host, MAX_HSTS_HOSTLEN) || + Curl_str_singlespace(&line) || + Curl_str_quotedword(&line, &date, MAX_HSTS_DATELEN) || + Curl_str_newline(&line)) + ; + else { CURLcode result = CURLE_OK; - char *p = host; bool subdomain = FALSE; struct stsentry *e; - if(p[0] == '.') { - p++; + char dbuf[MAX_HSTS_DATELEN + 1]; + time_t expires; + + /* The date parser works on a null terminated string. The maximum length + is upheld by Curl_str_quotedword(). */ + memcpy(dbuf, date.str, date.len); + dbuf[date.len] = 0; + + expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) : + TIME_T_MAX; + + if(host.str[0] == '.') { + host.str++; + host.len--; subdomain = TRUE; } /* only add it if not already present */ - e = Curl_hsts(h, p, subdomain); + e = Curl_hsts(h, host.str, host.len, subdomain); if(!e) - result = hsts_create(h, p, subdomain, expires); - else if(strcasecompare(p, e->host)) { + result = hsts_create(h, host.str, host.len, subdomain, expires); + else if((strlen(e->host) == host.len) && + strncasecompare(host.str, e->host, host.len)) { /* the same hostname, use the largest expire time */ if(expires > e->expires) e->expires = expires; @@ -488,7 +497,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) expires = Curl_getdate_capped(e.expire); else expires = TIME_T_MAX; /* the end of time */ - result = hsts_create(h, e.name, + result = hsts_create(h, e.name, strlen(e.name), /* bitfield to bool conversion: */ e.includeSubDomains ? TRUE : FALSE, expires); diff --git a/lib/hsts.h b/lib/hsts.h index 1c544f9..e8d0f9d 100644 --- a/lib/hsts.h +++ b/lib/hsts.h @@ -52,7 +52,7 @@ void Curl_hsts_cleanup(struct hsts **hp); CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, const char *sts); struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - bool subdomain); + size_t hlen, bool subdomain); CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, const char *file); CURLcode Curl_hsts_loadfile(struct Curl_easy *data, diff --git a/lib/http.c b/lib/http.c index 35e7085..1fe0904 100644 --- a/lib/http.c +++ b/lib/http.c @@ -47,10 +47,6 @@ #include #endif -#ifdef USE_HYPER -#include -#endif - #include "urldata.h" #include #include "transfer.h" @@ -68,6 +64,7 @@ #include "http_negotiate.h" #include "http_aws_sigv4.h" #include "url.h" +#include "urlapi-int.h" #include "share.h" #include "hostip.h" #include "dynhds.h" @@ -88,7 +85,6 @@ #include "altsvc.h" #include "hsts.h" #include "ws.h" -#include "c-hyper.h" #include "curl_ctype.h" /* The last 3 #include files should be in this order */ @@ -104,6 +100,30 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode); static bool http_exp100_is_waiting(struct Curl_easy *data); static CURLcode http_exp100_add_reader(struct Curl_easy *data); static void http_exp100_send_anyway(struct Curl_easy *data); +static bool http_exp100_is_selected(struct Curl_easy *data); +static void http_exp100_got100(struct Curl_easy *data); +static CURLcode http_firstwrite(struct Curl_easy *data); +static CURLcode http_header(struct Curl_easy *data, + const char *hd, size_t hdlen); +static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn); +static CURLcode http_range(struct Curl_easy *data, + Curl_HttpReq httpreq); +static CURLcode http_req_complete(struct Curl_easy *data, + struct dynbuf *r, int httpversion, + Curl_HttpReq httpreq); +static CURLcode http_req_set_reader(struct Curl_easy *data, + Curl_HttpReq httpreq, int httpversion, + const char **tep); +static CURLcode http_size(struct Curl_easy *data); +static CURLcode http_statusline(struct Curl_easy *data, + struct connectdata *conn); +static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn, + struct dynbuf *req); +static CURLcode http_useragent(struct Curl_easy *data); +#ifdef HAVE_LIBZ +static CURLcode http_transferencode(struct Curl_easy *data); +#endif + /* * HTTP handler interface. @@ -126,6 +146,7 @@ const struct Curl_handler Curl_handler_http = { Curl_http_write_resp_hd, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ PORT_HTTP, /* defport */ CURLPROTO_HTTP, /* protocol */ CURLPROTO_HTTP, /* family */ @@ -155,6 +176,7 @@ const struct Curl_handler Curl_handler_https = { Curl_http_write_resp_hd, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ PORT_HTTPS, /* defport */ CURLPROTO_HTTPS, /* protocol */ CURLPROTO_HTTP, /* family */ @@ -510,8 +532,10 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) pickhost = pickoneauth(&data->state.authhost, authmask); if(!pickhost) data->state.authproblem = TRUE; + else + data->info.httpauthpicked = data->state.authhost.picked; if(data->state.authhost.picked == CURLAUTH_NTLM && - conn->httpversion > 11) { + (data->req.httpversion_sent > 11)) { infof(data, "Forcing HTTP/1.1 for NTLM"); connclose(conn, "Force HTTP/1.1 connection"); data->state.httpwant = CURL_HTTP_VERSION_1_1; @@ -525,6 +549,9 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) authmask & ~CURLAUTH_BEARER); if(!pickproxy) data->state.authproblem = TRUE; + else + data->info.proxyauthpicked = data->state.authproxy.picked; + } #endif @@ -831,12 +858,12 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, struct connectdata *conn = data->conn; #ifdef USE_SPNEGO curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state : - &conn->http_negotiate_state; + &conn->http_negotiate_state; #endif -#if defined(USE_SPNEGO) || \ - defined(USE_NTLM) || \ - !defined(CURL_DISABLE_DIGEST_AUTH) || \ - !defined(CURL_DISABLE_BASIC_AUTH) || \ +#if defined(USE_SPNEGO) || \ + defined(USE_NTLM) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_BASIC_AUTH) || \ !defined(CURL_DISABLE_BEARER_AUTH) unsigned long *availp; @@ -967,7 +994,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, authp->avail |= CURLAUTH_BEARER; if(authp->picked == CURLAUTH_BEARER) { /* We asked for Bearer authentication but got a 40X back - anyway, which basically means our token is not valid. */ + anyway, which basically means our token is not valid. */ authp->avail = CURLAUTH_NONE; infof(data, "Authentication problem. Ignoring this."); data->state.authproblem = TRUE; @@ -1067,6 +1094,283 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) return data->state.authproblem; } +CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, + followtype type) +{ + bool disallowport = FALSE; + bool reachedmax = FALSE; + char *follow_url = NULL; + CURLUcode uc; + + DEBUGASSERT(type != FOLLOW_NONE); + + if(type != FOLLOW_FAKE) + data->state.requests++; /* count all real follows */ + if(type == FOLLOW_REDIR) { + if((data->set.maxredirs != -1) && + (data->state.followlocation >= data->set.maxredirs)) { + reachedmax = TRUE; + type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected + to URL */ + } + else { + data->state.followlocation++; /* count redirect-followings, including + auth reloads */ + + if(data->set.http_auto_referer) { + CURLU *u; + char *referer = NULL; + + /* We are asked to automatically set the previous URL as the referer + when we get the next URL. We pick the ->url field, which may or may + not be 100% correct */ + + if(data->state.referer_alloc) { + Curl_safefree(data->state.referer); + data->state.referer_alloc = FALSE; + } + + /* Make a copy of the URL without credentials and fragment */ + u = curl_url(); + if(!u) + return CURLE_OUT_OF_MEMORY; + + uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_USER, NULL, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0); + if(!uc) + uc = curl_url_get(u, CURLUPART_URL, &referer, 0); + + curl_url_cleanup(u); + + if(uc || !referer) + return CURLE_OUT_OF_MEMORY; + + data->state.referer = referer; + data->state.referer_alloc = TRUE; /* yes, free this later */ + } + } + } + + if((type != FOLLOW_RETRY) && + (data->req.httpcode != 401) && (data->req.httpcode != 407) && + Curl_is_absolute_url(newurl, NULL, 0, FALSE)) { + /* If this is not redirect due to a 401 or 407 response and an absolute + URL: do not allow a custom port number */ + disallowport = TRUE; + } + + DEBUGASSERT(data->state.uh); + uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int) + ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : + ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) | + CURLU_ALLOW_SPACE | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); + if(uc) { + if(type != FOLLOW_FAKE) { + failf(data, "The redirect target URL could not be parsed: %s", + curl_url_strerror(uc)); + return Curl_uc_to_curlcode(uc); + } + + /* the URL could not be parsed for some reason, but since this is FAKE + mode, just duplicate the field as-is */ + follow_url = strdup(newurl); + if(!follow_url) + return CURLE_OUT_OF_MEMORY; + } + else { + uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + + /* Clear auth if this redirects to a different port number or protocol, + unless permitted */ + if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { + char *portnum; + int port; + bool clear = FALSE; + + if(data->set.use_port && data->state.allow_port) + /* a custom port is used */ + port = (int)data->set.use_port; + else { + uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, + CURLU_DEFAULT_PORT); + if(uc) { + free(follow_url); + return Curl_uc_to_curlcode(uc); + } + port = atoi(portnum); + free(portnum); + } + if(port != data->info.conn_remote_port) { + infof(data, "Clear auth, redirects to port from %u to %u", + data->info.conn_remote_port, port); + clear = TRUE; + } + else { + char *scheme; + const struct Curl_handler *p; + uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); + if(uc) { + free(follow_url); + return Curl_uc_to_curlcode(uc); + } + + p = Curl_get_scheme_handler(scheme); + if(p && (p->protocol != data->info.conn_protocol)) { + infof(data, "Clear auth, redirects scheme from %s to %s", + data->info.conn_scheme, scheme); + clear = TRUE; + } + free(scheme); + } + if(clear) { + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); + } + } + } + DEBUGASSERT(follow_url); + + if(type == FOLLOW_FAKE) { + /* we are only figuring out the new URL if we would have followed locations + but now we are done so we can get out! */ + data->info.wouldredirect = follow_url; + + if(reachedmax) { + failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs); + return CURLE_TOO_MANY_REDIRECTS; + } + return CURLE_OK; + } + + if(disallowport) + data->state.allow_port = FALSE; + + if(data->state.url_alloc) + Curl_safefree(data->state.url); + + data->state.url = follow_url; + data->state.url_alloc = TRUE; + Curl_req_soft_reset(&data->req, data); + infof(data, "Issue another request to this URL: '%s'", data->state.url); + + /* + * We get here when the HTTP code is 300-399 (and 401). We need to perform + * differently based on exactly what return code there was. + * + * News from 7.10.6: we can also get here on a 401 or 407, in case we act on + * an HTTP (proxy-) authentication scheme other than Basic. + */ + switch(data->info.httpcode) { + /* 401 - Act on a WWW-Authenticate, we keep on moving and do the + Authorization: XXXX header in the HTTP request code snippet */ + /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the + Proxy-Authorization: XXXX header in the HTTP request code snippet */ + /* 300 - Multiple Choices */ + /* 306 - Not used */ + /* 307 - Temporary Redirect */ + default: /* for all above (and the unknown ones) */ + /* Some codes are explicitly mentioned since I have checked RFC2616 and + * they seem to be OK to POST to. + */ + break; + case 301: /* Moved Permanently */ + /* (quote from RFC7231, section 6.4.2) + * + * Note: For historical reasons, a user agent MAY change the request + * method from POST to GET for the subsequent request. If this + * behavior is undesired, the 307 (Temporary Redirect) status code + * can be used instead. + * + * ---- + * + * Many webservers expect this, so these servers often answers to a POST + * request with an error page. To be sure that libcurl gets the page that + * most user agents would get, libcurl has to force GET. + * + * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and + * can be overridden with CURLOPT_POSTREDIR. + */ + if((data->state.httpreq == HTTPREQ_POST + || data->state.httpreq == HTTPREQ_POST_FORM + || data->state.httpreq == HTTPREQ_POST_MIME) + && !(data->set.keep_post & CURL_REDIR_POST_301)) { + infof(data, "Switch from POST to GET"); + data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); + } + break; + case 302: /* Found */ + /* (quote from RFC7231, section 6.4.3) + * + * Note: For historical reasons, a user agent MAY change the request + * method from POST to GET for the subsequent request. If this + * behavior is undesired, the 307 (Temporary Redirect) status code + * can be used instead. + * + * ---- + * + * Many webservers expect this, so these servers often answers to a POST + * request with an error page. To be sure that libcurl gets the page that + * most user agents would get, libcurl has to force GET. + * + * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and + * can be overridden with CURLOPT_POSTREDIR. + */ + if((data->state.httpreq == HTTPREQ_POST + || data->state.httpreq == HTTPREQ_POST_FORM + || data->state.httpreq == HTTPREQ_POST_MIME) + && !(data->set.keep_post & CURL_REDIR_POST_302)) { + infof(data, "Switch from POST to GET"); + data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); + } + break; + + case 303: /* See Other */ + /* 'See Other' location is not the resource but a substitute for the + * resource. In this case we switch the method to GET/HEAD, unless the + * method is POST and the user specified to keep it as POST. + * https://github.com/curl/curl/issues/5237#issuecomment-614641049 + */ + if(data->state.httpreq != HTTPREQ_GET && + ((data->state.httpreq != HTTPREQ_POST && + data->state.httpreq != HTTPREQ_POST_FORM && + data->state.httpreq != HTTPREQ_POST_MIME) || + !(data->set.keep_post & CURL_REDIR_POST_303))) { + data->state.httpreq = HTTPREQ_GET; + infof(data, "Switch to %s", + data->req.no_body ? "HEAD" : "GET"); + } + break; + case 304: /* Not Modified */ + /* 304 means we did a conditional request and it was "Not modified". + * We should not get any Location: header in this response! + */ + break; + case 305: /* Use Proxy */ + /* (quote from RFC2616, section 10.3.6): + * "The requested resource MUST be accessed through the proxy given + * by the Location field. The Location field gives the URI of the + * proxy. The recipient is expected to repeat this single request + * via the proxy. 305 responses MUST only be generated by origin + * servers." + */ + break; + } + Curl_pgrsTime(data, TIMER_REDIRECT); + Curl_pgrsResetTransferSizes(data); + + return CURLE_OK; +} + /* * Curl_compareheader() * @@ -1169,7 +1473,6 @@ CURLcode Curl_http_done(struct Curl_easy *data, data->state.authproxy.multipass = FALSE; Curl_dyn_reset(&data->state.headerb); - Curl_hyper_done(data); if(status) return status; @@ -1193,52 +1496,55 @@ CURLcode Curl_http_done(struct Curl_easy *data, return CURLE_OK; } -/* - * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons - * to avoid it include: - * - * - if the user specifically requested HTTP 1.0 - * - if the server we are connected to only supports 1.0 - * - if any server previously contacted to handle this request only supports - * 1.0. - */ -bool Curl_use_http_1_1plus(const struct Curl_easy *data, - const struct connectdata *conn) +/* Determine if we may use HTTP 1.1 for this request. */ +static bool http_may_use_1_1(const struct Curl_easy *data) { - if((data->state.httpversion == 10) || (conn->httpversion == 10)) + const struct connectdata *conn = data->conn; + /* We have seen a previous response for *this* transfer with 1.0, + * on another connection or the same one. */ + if(data->state.httpversion == 10) + return FALSE; + /* We have seen a previous response on *this* connection with 1.0. */ + if(conn->httpversion_seen == 10) return FALSE; + /* We want 1.0 and have seen no previous response on *this* connection + with a higher version (maybe no response at all yet). */ if((data->state.httpwant == CURL_HTTP_VERSION_1_0) && - (conn->httpversion <= 10)) + (conn->httpversion_seen <= 10)) return FALSE; - return ((data->state.httpwant == CURL_HTTP_VERSION_NONE) || - (data->state.httpwant >= CURL_HTTP_VERSION_1_1)); + /* We want something newer than 1.0 or have no preferences. */ + return (data->state.httpwant == CURL_HTTP_VERSION_NONE) || + (data->state.httpwant >= CURL_HTTP_VERSION_1_1); } -#ifndef USE_HYPER -static const char *get_http_string(const struct Curl_easy *data, - const struct connectdata *conn) +static unsigned char http_request_version(struct Curl_easy *data) { - if(Curl_conn_is_http3(data, conn, FIRSTSOCKET)) - return "3"; - if(Curl_conn_is_http2(data, conn, FIRSTSOCKET)) - return "2"; - if(Curl_use_http_1_1plus(data, conn)) - return "1.1"; - - return "1.0"; + unsigned char httpversion = Curl_conn_http_version(data); + if(!httpversion) { + /* No specific HTTP connection filter installed. */ + httpversion = http_may_use_1_1(data) ? 11 : 10; + } + return httpversion; +} + +static const char *get_http_string(int httpversion) +{ + switch(httpversion) { + case 30: + return "3"; + case 20: + return "2"; + case 11: + return "1.1"; + default: + return "1.0"; + } } -#endif CURLcode Curl_add_custom_headers(struct Curl_easy *data, - bool is_connect, -#ifndef USE_HYPER - struct dynbuf *req -#else - void *req -#endif - ) + bool is_connect, int httpversion, + struct dynbuf *req) { - struct connectdata *conn = data->conn; char *ptr; struct curl_slist *h[2]; struct curl_slist *headers; @@ -1251,7 +1557,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, if(is_connect) proxy = HEADER_CONNECT; else - proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ? + proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ? HEADER_PROXY : HEADER_SERVER; switch(proxy) { @@ -1303,9 +1609,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, /* copy the source */ semicolonp = strdup(headers->data); if(!semicolonp) { -#ifndef USE_HYPER Curl_dyn_free(req); -#endif return CURLE_OUT_OF_MEMORY; } /* put a colon where the semicolon is */ @@ -1353,7 +1657,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, Connection: */ checkprefix("Connection:", compare)) ; - else if((conn->httpversion >= 20) && + else if((httpversion >= 20) && checkprefix("Transfer-Encoding:", compare)) /* HTTP/2 does not support chunked requests */ ; @@ -1364,11 +1668,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, !Curl_auth_allowed_to_host(data)) ; else { -#ifdef USE_HYPER - result = Curl_hyper_header(data, req, compare); -#else result = Curl_dyn_addf(req, "%s\r\n", compare); -#endif } if(semicolonp) free(semicolonp); @@ -1385,12 +1685,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, #ifndef CURL_DISABLE_PARSEDATE CURLcode Curl_add_timecondition(struct Curl_easy *data, -#ifndef USE_HYPER - struct dynbuf *req -#else - void *req -#endif - ) + struct dynbuf *req) { const struct tm *tm; struct tm keeptime; @@ -1453,12 +1748,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, tm->tm_min, tm->tm_sec); -#ifndef USE_HYPER result = Curl_dyn_add(req, datestr); -#else - result = Curl_hyper_header(data, req, datestr); -#endif - return result; } #else @@ -1512,7 +1802,7 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, *reqp = httpreq; } -CURLcode Curl_http_useragent(struct Curl_easy *data) +static CURLcode http_useragent(struct Curl_easy *data) { /* The User-Agent string might have been allocated in url.c already, because it might have been used in the proxy connect, but if we have got a header @@ -1526,7 +1816,7 @@ CURLcode Curl_http_useragent(struct Curl_easy *data) } -CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) +static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn) { const char *ptr; struct dynamically_allocated_data *aptr = &data->state.aptr; @@ -1616,9 +1906,9 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) /* * Append the request-target to the HTTP request */ -CURLcode Curl_http_target(struct Curl_easy *data, - struct connectdata *conn, - struct dynbuf *r) +static CURLcode http_target(struct Curl_easy *data, + struct connectdata *conn, + struct dynbuf *r) { CURLcode result = CURLE_OK; const char *path = data->state.up.path; @@ -1684,7 +1974,7 @@ CURLcode Curl_http_target(struct Curl_easy *data, data->set.str[STRING_TARGET] : url); free(url); if(result) - return (result); + return result; if(strcasecompare("ftp", data->state.up.scheme)) { if(data->set.proxy_transfer_mode) { @@ -1898,9 +2188,9 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) return CURLE_OK; } -CURLcode Curl_http_req_set_reader(struct Curl_easy *data, - Curl_HttpReq httpreq, - const char **tep) +static CURLcode http_req_set_reader(struct Curl_easy *data, + Curl_HttpReq httpreq, int httpversion, + const char **tep) { CURLcode result = CURLE_OK; const char *ptr; @@ -1919,12 +2209,10 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data, data->req.upload_chunky = Curl_compareheader(ptr, STRCONST("Transfer-Encoding:"), STRCONST("chunked")); - if(data->req.upload_chunky && - Curl_use_http_1_1plus(data, data->conn) && - (data->conn->httpversion >= 20)) { - infof(data, "suppressing chunked transfer encoding on connection " - "using HTTP version 2 or higher"); - data->req.upload_chunky = FALSE; + if(data->req.upload_chunky && (httpversion >= 20)) { + infof(data, "suppressing chunked transfer encoding on connection " + "using HTTP version 2 or higher"); + data->req.upload_chunky = FALSE; } } else { @@ -1932,10 +2220,10 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data, if(req_clen < 0) { /* indeterminate request content length */ - if(Curl_use_http_1_1plus(data, data->conn)) { + if(httpversion > 10) { /* On HTTP/1.1, enable chunked, on HTTP/2 and later we do not * need it */ - data->req.upload_chunky = (data->conn->httpversion < 20); + data->req.upload_chunky = (httpversion < 20); } else { failf(data, "Chunky upload is not supported by HTTP 1.0"); @@ -1954,7 +2242,7 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data, } static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, - bool *announced_exp100) + int httpversion, bool *announced_exp100) { CURLcode result; char *ptr; @@ -1973,9 +2261,7 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, *announced_exp100 = Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); } - else if(!data->state.disableexpect && - Curl_use_http_1_1plus(data, data->conn) && - (data->conn->httpversion < 20)) { + else if(!data->state.disableexpect && (httpversion == 11)) { /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an Expect: 100-continue to the headers which actually speeds up post operations (as there is one packet coming back from the web server) */ @@ -1990,21 +2276,20 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, return CURLE_OK; } -CURLcode Curl_http_req_complete(struct Curl_easy *data, - struct dynbuf *r, Curl_HttpReq httpreq) +static CURLcode http_req_complete(struct Curl_easy *data, + struct dynbuf *r, int httpversion, + Curl_HttpReq httpreq) { CURLcode result = CURLE_OK; curl_off_t req_clen; bool announced_exp100 = FALSE; DEBUGASSERT(data->conn); -#ifndef USE_HYPER if(data->req.upload_chunky) { result = Curl_httpchunk_add_reader(data); if(result) return result; } -#endif /* Get the request body length that has been set up */ req_clen = Curl_creader_total_length(data); @@ -2053,7 +2338,7 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data, goto out; } } - result = addexpect(data, r, &announced_exp100); + result = addexpect(data, r, httpversion, &announced_exp100); if(result) goto out; break; @@ -2079,9 +2364,9 @@ out: #if !defined(CURL_DISABLE_COOKIES) -CURLcode Curl_http_cookies(struct Curl_easy *data, - struct connectdata *conn, - struct dynbuf *r) +static CURLcode http_cookies(struct Curl_easy *data, + struct connectdata *conn, + struct dynbuf *r) { CURLcode result = CURLE_OK; char *addcookies = NULL; @@ -2155,10 +2440,12 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, } return result; } +#else +#define http_cookies(a,b,c) CURLE_OK #endif -CURLcode Curl_http_range(struct Curl_easy *data, - Curl_HttpReq httpreq) +static CURLcode http_range(struct Curl_easy *data, + Curl_HttpReq httpreq) { if(data->state.use_range) { /* @@ -2214,7 +2501,7 @@ CURLcode Curl_http_range(struct Curl_easy *data, return CURLE_OK; } -CURLcode Curl_http_firstwrite(struct Curl_easy *data) +static CURLcode http_firstwrite(struct Curl_easy *data) { struct connectdata *conn = data->conn; struct SingleRequest *k = &data->req; @@ -2277,7 +2564,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data) } #ifdef HAVE_LIBZ -CURLcode Curl_transferencode(struct Curl_easy *data) +static CURLcode http_transferencode(struct Curl_easy *data) { if(!Curl_checkheaders(data, STRCONST("TE")) && data->set.http_transfer_encoding) { @@ -2309,7 +2596,6 @@ CURLcode Curl_transferencode(struct Curl_easy *data) } #endif -#ifndef USE_HYPER /* * Curl_http() gets called from the generic multi_do() function when an HTTP * request is to be performed. This creates and sends a properly constructed @@ -2326,6 +2612,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) struct dynbuf req; char *altused = NULL; const char *p_accept; /* Accept: string */ + unsigned char httpversion; /* Always consider the DO phase done after this function call, even if there may be parts of the request that are not yet sent, since we can deal with @@ -2334,29 +2621,29 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) switch(conn->alpn) { case CURL_HTTP_VERSION_3: - DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET)); + DEBUGASSERT(Curl_conn_http_version(data) == 30); break; case CURL_HTTP_VERSION_2: #ifndef CURL_DISABLE_PROXY - if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) && + if((Curl_conn_http_version(data) != 20) && conn->bits.proxy && !conn->bits.tunnel_proxy ) { - result = Curl_http2_switch(data, conn, FIRSTSOCKET); + result = Curl_http2_switch(data); if(result) goto fail; } else #endif - DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET)); + DEBUGASSERT(Curl_conn_http_version(data) == 20); break; case CURL_HTTP_VERSION_1_1: /* continue with HTTP/1.x when explicitly requested */ break; default: /* Check if user wants to use HTTP/2 with clear TCP */ - if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) { + if(Curl_http2_may_switch(data)) { DEBUGF(infof(data, "HTTP/2 over clean TCP")); - result = Curl_http2_switch(data, conn, FIRSTSOCKET); + result = Curl_http2_switch(data); if(result) goto fail; } @@ -2370,11 +2657,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(result) goto fail; - result = Curl_http_host(data, conn); + result = http_host(data, conn); if(result) goto fail; - result = Curl_http_useragent(data); + result = http_useragent(data); if(result) goto fail; @@ -2415,24 +2702,25 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) #ifdef HAVE_LIBZ /* we only consider transfer-encoding magic if libz support is built-in */ - result = Curl_transferencode(data); + result = http_transferencode(data); if(result) goto fail; #endif - result = Curl_http_req_set_reader(data, httpreq, &te); + httpversion = http_request_version(data); + httpstring = get_http_string(httpversion); + + result = http_req_set_reader(data, httpreq, httpversion, &te); if(result) goto fail; p_accept = Curl_checkheaders(data, STRCONST("Accept")) ? NULL : "Accept: */*\r\n"; - result = Curl_http_range(data, httpreq); + result = http_range(data, httpreq); if(result) goto fail; - httpstring = get_http_string(data, conn); - /* initialize a dynamic send-buffer */ Curl_dyn_init(&req, DYN_HTTP_REQUEST); @@ -2444,7 +2732,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* GET/HEAD/POST/PUT */ result = Curl_dyn_addf(&req, "%s ", request); if(!result) - result = Curl_http_target(data, conn, &req); + result = http_target(data, conn, &req); if(result) { Curl_dyn_free(&req); goto fail; @@ -2526,8 +2814,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto fail; } - if(!(conn->handler->flags&PROTOPT_SSL) && - conn->httpversion < 20 && + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) && (data->state.httpwant == CURL_HTTP_VERSION_2)) { /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done over SSL */ @@ -2538,7 +2825,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } } - result = Curl_http_cookies(data, conn, &req); + result = http_cookies(data, conn, &req); #ifndef CURL_DISABLE_WEBSOCKETS if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) result = Curl_ws_request(data, &req); @@ -2546,19 +2833,19 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!result) result = Curl_add_timecondition(data, &req); if(!result) - result = Curl_add_custom_headers(data, FALSE, &req); + result = Curl_add_custom_headers(data, FALSE, httpversion, &req); if(!result) { /* req_send takes ownership of the 'req' memory on success */ - result = Curl_http_req_complete(data, &req, httpreq); + result = http_req_complete(data, &req, httpversion, httpreq); if(!result) - result = Curl_req_send(data, &req); + result = Curl_req_send(data, &req, httpversion); } Curl_dyn_free(&req); if(result) goto fail; - if((conn->httpversion >= 20) && data->req.upload_chunky) + if((httpversion >= 20) && data->req.upload_chunky) /* upload_chunky was set above to set up the request in a chunky fashion, but is disabled here again to avoid that the chunked encoded version is actually used when sending the request body over h2 */ @@ -2569,8 +2856,6 @@ fail: return result; } -#endif /* USE_HYPER */ - typedef enum { STATUS_UNKNOWN, /* not enough data to tell yet */ STATUS_DONE, /* a status line was read */ @@ -2657,10 +2942,10 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn, Curl_compareheader(hd, STRCONST(n), STRCONST(v))) /* - * Curl_http_header() parses a single response header. + * http_header() parses a single response header. */ -CURLcode Curl_http_header(struct Curl_easy *data, - const char *hd, size_t hdlen) +static CURLcode http_header(struct Curl_easy *data, + const char *hd, size_t hdlen) { struct connectdata *conn = data->conn; CURLcode result; @@ -2672,7 +2957,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, case 'A': #ifndef CURL_DISABLE_ALTSVC v = (data->asi && - ((data->conn->handler->flags & PROTOPT_SSL) || + (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") @@ -2682,8 +2967,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, )) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL; if(v) { /* the ALPN of the current request */ - enum alpnid id = (conn->httpversion == 30) ? ALPN_h3 : - (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; + enum alpnid id = (k->httpversion == 30) ? ALPN_h3 : + (k->httpversion == 20) ? ALPN_h2 : ALPN_h1; return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name, curlx_uitous((unsigned int)conn->remote_port)); } @@ -2755,7 +3040,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, streamclose(conn, "Connection: close used"); return CURLE_OK; } - if((conn->httpversion == 10) && + if((k->httpversion == 10) && HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) { /* * An HTTP/1.0 reply with the 'Connection: keep-alive' line @@ -2845,7 +3130,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY v = HD_VAL(hd, hdlen, "Proxy-Connection:"); if(v) { - if((conn->httpversion == 10) && conn->bits.httpproxy && + if((k->httpversion == 10) && conn->bits.httpproxy && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { /* * When an HTTP/1.0 reply comes when using a proxy, the @@ -2856,7 +3141,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */ infof(data, "HTTP/1.0 proxy connection set to keep alive"); } - else if((conn->httpversion == 11) && conn->bits.httpproxy && + else if((k->httpversion == 11) && conn->bits.httpproxy && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { /* * We get an HTTP/1.1 response from a proxy and it says it will @@ -2903,11 +3188,19 @@ CURLcode Curl_http_header(struct Curl_easy *data, (void)curlx_strtoofft(v, NULL, 10, &retry_after); if(!retry_after) { time_t date = Curl_getdate_capped(v); - if(-1 != date) + time_t current = time(NULL); + if((time_t)-1 != date && date > current) { /* convert date to number of seconds into the future */ - retry_after = date - time(NULL); + retry_after = date - current; + } } - data->info.retry_after = retry_after; /* store it */ + if(retry_after < 0) + retry_after = 0; + /* limit to 6 hours max. this is not documented so that it can be changed + in the future if necessary. */ + if(retry_after > 21600) + retry_after = 21600; + data->info.retry_after = retry_after; return CURLE_OK; } break; @@ -2938,7 +3231,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, #ifndef CURL_DISABLE_HSTS /* If enabled, the header is incoming and this is over HTTPS */ v = (data->hsts && - ((conn->handler->flags & PROTOPT_SSL) || + (Curl_conn_is_ssl(conn, FIRSTSOCKET) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_HSTS_HTTP") @@ -3022,8 +3315,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, * Called after the first HTTP response line (the status line) has been * received and parsed. */ -CURLcode Curl_http_statusline(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode http_statusline(struct Curl_easy *data, + struct connectdata *conn) { struct SingleRequest *k = &data->req; @@ -3037,11 +3330,11 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, case 30: #endif /* no major version switch mid-connection */ - if(conn->httpversion && - (k->httpversion/10 != conn->httpversion/10)) { + if(k->httpversion_sent && + (k->httpversion/10 != k->httpversion_sent/10)) { failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)", - conn->httpversion/10, k->httpversion/10); - return CURLE_UNSUPPORTED_PROTOCOL; + k->httpversion_sent/10, k->httpversion/10); + return CURLE_WEIRD_SERVER_REPLY; } break; default: @@ -3052,7 +3345,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, data->info.httpcode = k->httpcode; data->info.httpversion = k->httpversion; - conn->httpversion = (unsigned char)k->httpversion; + conn->httpversion_seen = (unsigned char)k->httpversion; if(!data->state.httpversion || data->state.httpversion > k->httpversion) /* store the lowest server version we encounter */ @@ -3116,7 +3409,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, figured out here after all headers have been received but before the final call to the user's header callback, so that a valid content length can be retrieved by the user in the final call. */ -CURLcode Curl_http_size(struct Curl_easy *data) +static CURLcode http_size(struct Curl_easy *data) { struct SingleRequest *k = &data->req; if(data->req.ignore_cl || k->chunk) { @@ -3232,7 +3525,7 @@ static CURLcode http_on_response(struct Curl_easy *data, if(k->upgr101 == UPGR101_RECEIVED) { /* supposedly upgraded to http2 now */ - if(conn->httpversion != 20) + if(data->req.httpversion != 20) infof(data, "Lying server, not serving HTTP/2"); } @@ -3264,12 +3557,11 @@ static CURLcode http_on_response(struct Curl_easy *data, * that tells us that the server is OK with this and ready * to receive the data. */ - Curl_http_exp100_got100(data); + http_exp100_got100(data); break; case 101: /* Switching Protocols only allowed from HTTP/1.1 */ - - if(conn->httpversion != 11) { + if(k->httpversion_sent != 11) { /* invalid for other HTTP versions */ failf(data, "unexpected 101 response code"); result = CURLE_WEIRD_SERVER_REPLY; @@ -3283,6 +3575,7 @@ static CURLcode http_on_response(struct Curl_easy *data, /* We expect more response from HTTP/2 later */ k->header = TRUE; k->headerline = 0; /* restart the header line counter */ + k->httpversion_sent = 20; /* It's a HTTP/2 request now */ /* Any remaining `buf` bytes are already HTTP/2 and passed to * be processed. */ result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); @@ -3331,7 +3624,7 @@ static CURLcode http_on_response(struct Curl_easy *data, } if((k->size == -1) && !k->chunk && !conn->bits.close && - (conn->httpversion == 11) && + (k->httpversion == 11) && !(conn->handler->protocol & CURLPROTO_RTSP) && data->state.httpreq != HTTPREQ_HEAD) { /* On HTTP 1.1, when connection is not to get closed, but no @@ -3423,7 +3716,7 @@ static CURLcode http_on_response(struct Curl_easy *data, * connection for closure after we have read the entire response. */ if(!Curl_req_done_sending(data)) { - if((k->httpcode == 417) && Curl_http_exp100_is_selected(data)) { + if((k->httpcode == 417) && http_exp100_is_selected(data)) { /* 417 Expectation Failed - try again without the Expect header */ if(!k->writebytecount && http_exp100_is_waiting(data)) { @@ -3480,19 +3773,17 @@ static CURLcode http_on_response(struct Curl_easy *data, like to call http2_handle_stream_close to properly close a stream. In order to do this, we keep reading until we close the stream. */ - if(0 == k->maxdownload - && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) - && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) + if((0 == k->maxdownload) && (k->httpversion_sent < 20)) k->download_done = TRUE; /* final response without error, prepare to receive the body */ - result = Curl_http_firstwrite(data); + result = http_firstwrite(data); if(!result) /* This is the last response that we get for the current request. * Check on the body size and determine if the response is complete. */ - result = Curl_http_size(data); + result = http_size(data); out: if(last_hd) { @@ -3567,7 +3858,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, p++; if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) { if(ISBLANK(p[2])) { - k->httpversion = 10 + (p[1] - '0'); + k->httpversion = (unsigned char)(10 + (p[1] - '0')); p += 3; if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + @@ -3587,7 +3878,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, case '3': if(!ISBLANK(p[1])) break; - k->httpversion = (*p - '0') * 10; + k->httpversion = (unsigned char)((*p - '0') * 10); p += 2; if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + @@ -3645,7 +3936,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, } if(fine_statusline) { - result = Curl_http_statusline(data, data->conn); + result = http_statusline(data, data->conn); if(result) return result; writetype |= CLIENTWRITE_STATUS; @@ -3660,7 +3951,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, if(result) return result; - result = Curl_http_header(data, hd, hdlen); + result = http_header(data, hd, hdlen); if(result) return result; @@ -3717,10 +4008,11 @@ static CURLcode http_parse_headers(struct Curl_easy *data, Curl_dyn_len(&data->state.headerb)); if(st == STATUS_BAD) { - /* this is not the beginning of a protocol first header line */ + /* this is not the beginning of a protocol first header line. + * Cannot be 0.9 if version was detected or connection was reused. */ k->header = FALSE; streamclose(conn, "bad HTTP: No end-of-message indicator"); - if(conn->httpversion >= 10) { + if((k->httpversion >= 10) || conn->bits.reuse) { failf(data, "Invalid status line"); return CURLE_WEIRD_SERVER_REPLY; } @@ -3755,8 +4047,9 @@ static CURLcode http_parse_headers(struct Curl_easy *data, Curl_dyn_len(&data->state.headerb)); if(st == STATUS_BAD) { streamclose(conn, "bad HTTP: No end-of-message indicator"); - /* this is not the beginning of a protocol first header line */ - if(conn->httpversion >= 10) { + /* this is not the beginning of a protocol first header line. + * Cannot be 0.9 if version was detected or connection was reused. */ + if((k->httpversion >= 10) || conn->bits.reuse) { failf(data, "Invalid status line"); return CURLE_WEIRD_SERVER_REPLY; } @@ -4115,7 +4408,9 @@ struct name_const { size_t namelen; }; +/* keep them sorted by length! */ static struct name_const H2_NON_FIELD[] = { + { STRCONST("TE") }, { STRCONST("Host") }, { STRCONST("Upgrade") }, { STRCONST("Connection") }, @@ -4160,7 +4455,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme); } else { - scheme = (data->conn && data->conn->handler->flags & PROTOPT_SSL) ? + scheme = Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ? "https" : "http"; } } @@ -4359,7 +4654,7 @@ static CURLcode http_exp100_add_reader(struct Curl_easy *data) return result; } -void Curl_http_exp100_got100(struct Curl_easy *data) +static void http_exp100_got100(struct Curl_easy *data) { struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); if(r) @@ -4371,7 +4666,7 @@ static bool http_exp100_is_waiting(struct Curl_easy *data) struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); if(r) { struct cr_exp100_ctx *ctx = r->ctx; - return (ctx->state == EXP100_AWAITING_CONTINUE); + return ctx->state == EXP100_AWAITING_CONTINUE; } return FALSE; } @@ -4383,7 +4678,7 @@ static void http_exp100_send_anyway(struct Curl_easy *data) http_exp100_continue(data, r); } -bool Curl_http_exp100_is_selected(struct Curl_easy *data) +static bool http_exp100_is_selected(struct Curl_easy *data) { struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); return !!r; diff --git a/lib/http.h b/lib/http.h index 7056e8a..a15a982 100644 --- a/lib/http.h +++ b/lib/http.h @@ -42,6 +42,18 @@ typedef enum { HTTPREQ_HEAD } Curl_HttpReq; + +/* When redirecting transfers. */ +typedef enum { + FOLLOW_NONE, /* not used within the function, just a placeholder to + allow initing to this */ + FOLLOW_FAKE, /* only records stuff, not actually following */ + FOLLOW_RETRY, /* set if this is a request retry as opposed to a real + redirect following */ + FOLLOW_REDIR /* a full true redirect */ +} followtype; + + #ifndef CURL_DISABLE_HTTP #if defined(USE_HTTP3) @@ -74,50 +86,14 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, const char *thisheader, const size_t thislen); -CURLcode Curl_add_timecondition(struct Curl_easy *data, -#ifndef USE_HYPER - struct dynbuf *req -#else - void *headers -#endif - ); -CURLcode Curl_add_custom_headers(struct Curl_easy *data, - bool is_connect, -#ifndef USE_HYPER - struct dynbuf *req -#else - void *headers -#endif - ); +CURLcode Curl_add_timecondition(struct Curl_easy *data, struct dynbuf *req); +CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect, + int httpversion, struct dynbuf *req); +CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect, + struct dynhds *hds); void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, const char **method, Curl_HttpReq *); -CURLcode Curl_http_useragent(struct Curl_easy *data); -CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn); -CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn, - struct dynbuf *req); -CURLcode Curl_http_statusline(struct Curl_easy *data, - struct connectdata *conn); -CURLcode Curl_http_header(struct Curl_easy *data, - const char *hd, size_t hdlen); -CURLcode Curl_transferencode(struct Curl_easy *data); -CURLcode Curl_http_req_set_reader(struct Curl_easy *data, - Curl_HttpReq httpreq, - const char **tep); -CURLcode Curl_http_req_complete(struct Curl_easy *data, - struct dynbuf *r, Curl_HttpReq httpreq); -bool Curl_use_http_1_1plus(const struct Curl_easy *data, - const struct connectdata *conn); -#ifndef CURL_DISABLE_COOKIES -CURLcode Curl_http_cookies(struct Curl_easy *data, - struct connectdata *conn, - struct dynbuf *r); -#else -#define Curl_http_cookies(a,b,c) CURLE_OK -#endif -CURLcode Curl_http_range(struct Curl_easy *data, - Curl_HttpReq httpreq); -CURLcode Curl_http_firstwrite(struct Curl_easy *data); /* protocol-specific functions set up to be called by the main engine */ CURLcode Curl_http_setup_conn(struct Curl_easy *data, @@ -139,6 +115,10 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, const char *auth); CURLcode Curl_http_auth_act(struct Curl_easy *data); +/* follow a redirect or not */ +CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, + followtype type); + /* If only the PICKNONE bit is set, there has been a round-trip and we selected to use no auth at all. Ie, we actively select no auth, as opposed to not having one selected. The other CURLAUTH_* defines are present in the @@ -175,8 +155,6 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data); version. This count includes CONNECT response headers. */ #define MAX_HTTP_RESP_HEADER_SIZE (300*1024) -bool Curl_http_exp100_is_selected(struct Curl_easy *data); -void Curl_http_exp100_got100(struct Curl_easy *data); #endif /* CURL_DISABLE_HTTP */ @@ -184,8 +162,6 @@ void Curl_http_exp100_got100(struct Curl_easy *data); * HTTP unique setup ***************************************************************************/ -CURLcode Curl_http_size(struct Curl_easy *data); - CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, const char *buf, size_t blen, size_t *pconsumed); diff --git a/lib/http2.c b/lib/http2.c index dbe6f1a..821f288 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -198,7 +198,6 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, * All about the H2 internals of a stream */ struct h2_stream_ctx { - struct bufq recvbuf; /* response buffer */ struct bufq sendbuf; /* request buffer */ struct h1_req_parser h1; /* parsing the request */ struct dynhds resp_trailers; /* response trailer fields */ @@ -240,7 +239,6 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); - stream->resp_hds_len = 0; stream->bodystarted = FALSE; stream->status_code = -1; stream->closed = FALSE; @@ -2057,23 +2055,10 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } if(nread > 0) { - size_t data_consumed = (size_t)nread; /* Now that we transferred this to the upper layer, we report * the actual amount of DATA consumed to the H2 session, so * that it adjusts stream flow control */ - if(stream->resp_hds_len >= data_consumed) { - stream->resp_hds_len -= data_consumed; /* no DATA */ - } - else { - if(stream->resp_hds_len) { - data_consumed -= stream->resp_hds_len; - stream->resp_hds_len = 0; - } - if(data_consumed) { - nghttp2_session_consume(ctx->h2, stream->id, data_consumed); - } - } - + nghttp2_session_consume(ctx->h2, stream->id, (size_t)nread); if(stream->closed) { CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id); drain_stream(cf, data, stream); @@ -2337,7 +2322,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, 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) { + if(stream && stream->closed) { nwritten = http2_handle_stream_close(cf, data, stream, err); } else { @@ -2496,9 +2481,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, /* Send out our SETTINGS and ACKs and such. If that blocks, we * have it buffered and can count this filter as being connected */ result = h2_progress_egress(cf, data); - if(result == CURLE_AGAIN) - result = CURLE_OK; - else if(result) + if(result && (result != CURLE_AGAIN)) goto out; *done = TRUE; @@ -2638,9 +2621,6 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, case CF_CTRL_FLUSH: result = cf_h2_flush(cf, data); break; - case CF_CTRL_DATA_DETACH: - http2_data_done(cf, data); - break; case CF_CTRL_DATA_DONE: http2_data_done(cf, data); break; @@ -2655,10 +2635,8 @@ static bool cf_h2_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; - struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); - if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq) - || (stream && !Curl_bufq_is_empty(&stream->sendbuf)))) + if(ctx && !Curl_bufq_is_empty(&ctx->inbufq)) return TRUE; return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; } @@ -2728,6 +2706,9 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, } break; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 20; + return CURLE_OK; default: break; } @@ -2738,7 +2719,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_nghttp2 = { "HTTP/2", - CF_TYPE_MULTIPLEX, + CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, CURL_LOG_LVL_NONE, cf_h2_destroy, cf_h2_connect, @@ -2812,35 +2793,12 @@ out: return result; } -static bool cf_is_http2(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_nghttp2) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -bool Curl_conn_is_http2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - return conn ? cf_is_http2(conn->cfilter[sockindex], data) : FALSE; -} - -bool Curl_http2_may_switch(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) +bool Curl_http2_may_switch(struct Curl_easy *data) { - (void)sockindex; - if(!Curl_conn_is_http2(data, conn, sockindex) && + if(Curl_conn_http_version(data) < 20 && data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { #ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) { /* We do not support HTTP/2 proxies yet. Also it is debatable whether or not this setting should apply to HTTP/2 proxies. */ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); @@ -2852,21 +2810,19 @@ bool Curl_http2_may_switch(struct Curl_easy *data, return FALSE; } -CURLcode Curl_http2_switch(struct Curl_easy *data, - struct connectdata *conn, int sockindex) +CURLcode Curl_http2_switch(struct Curl_easy *data) { struct Curl_cfilter *cf; CURLcode result; - DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); + DEBUGASSERT(Curl_conn_http_version(data) < 20); - result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE); + result = http2_cfilter_add(&cf, data, data->conn, FIRSTSOCKET, 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 */ + data->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ Curl_multi_connchanged(data->multi); if(cf->next) { @@ -2881,14 +2837,13 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cfilter *cf_h2; CURLcode result; - DEBUGASSERT(!cf_is_http2(cf, data)); + DEBUGASSERT(Curl_conn_http_version(data) < 20); result = http2_cfilter_insert_after(cf, data, FALSE); if(result) return result; 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 */ Curl_multi_connchanged(data->multi); @@ -2907,7 +2862,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, struct cf_h2_ctx *ctx; CURLcode result; - DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); + DEBUGASSERT(Curl_conn_http_version(data) < 20); DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE); @@ -2940,7 +2895,6 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, " after upgrade: len=%zu", nread); } - conn->httpversion = 20; /* we know we are on HTTP/2 now */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ Curl_multi_connchanged(data->multi); @@ -2955,9 +2909,9 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, CURLE_HTTP2_STREAM error! */ bool Curl_h2_http_1_1_error(struct Curl_easy *data) { - if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) { + if(Curl_conn_http_version(data) == 20) { int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET); - return (err == NGHTTP2_HTTP_1_1_REQUIRED); + return err == NGHTTP2_HTTP_1_1_REQUIRED; } return FALSE; } diff --git a/lib/http2.h b/lib/http2.h index dbb1784..93cc2d4 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -44,15 +44,9 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, /* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */ bool Curl_h2_http_1_1_error(struct Curl_easy *data); -bool Curl_conn_is_http2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); -bool Curl_http2_may_switch(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); +bool Curl_http2_may_switch(struct Curl_easy *data); -CURLcode Curl_http2_switch(struct Curl_easy *data, - struct connectdata *conn, int sockindex); +CURLcode Curl_http2_switch(struct Curl_easy *data); CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data); @@ -69,12 +63,10 @@ extern struct Curl_cftype Curl_cft_nghttp2; #else /* USE_NGHTTP2 */ -#define Curl_cf_is_http2(a,b) FALSE -#define Curl_conn_is_http2(a,b,c) FALSE -#define Curl_http2_may_switch(a,b,c) FALSE +#define Curl_http2_may_switch(a) FALSE #define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_switch(a,b,c) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_switch(a) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL #define Curl_h2_http_1_1_error(x) 0 #endif diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 5d4848f..c217d0d 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -35,6 +35,7 @@ #include "parsedate.h" #include "sendf.h" #include "escape.h" +#include "strparse.h" #include @@ -118,8 +119,6 @@ static void trim_headers(struct curl_slist *head) /* maximum length for the aws sivg4 parts */ #define MAX_SIGV4_LEN 64 -#define MAX_SIGV4_LEN_TXT "64" - #define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date")) /* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */ @@ -160,7 +159,8 @@ static int compare_header_names(const char *a, const char *b) static CURLcode make_headers(struct Curl_easy *data, const char *hostname, char *timestamp, - char *provider1, + const char *provider1, + size_t plen, /* length of provider1 */ char **date_header, char *content_sha256_header, struct dynbuf *canonical_headers, @@ -174,16 +174,16 @@ static CURLcode make_headers(struct Curl_easy *data, struct curl_slist *l; bool again = TRUE; - /* provider1 mid */ - Curl_strntolower(provider1, provider1, strlen(provider1)); - provider1[0] = Curl_raw_toupper(provider1[0]); - - msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1); + msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%.*s-Date", + (int)plen, provider1); + /* provider1 ucfirst */ + Curl_strntolower(&date_hdr_key[2], provider1, plen); + date_hdr_key[2] = Curl_raw_toupper(provider1[0]); - /* provider1 lowercase */ - Curl_strntolower(provider1, provider1, 1); /* first byte only */ msnprintf(date_full_hdr, DATE_FULL_HDR_LEN, - "x-%s-date:%s", provider1, timestamp); + "x-%.*s-date:%s", (int)plen, provider1, timestamp); + /* provider1 lowercase */ + Curl_strntolower(&date_full_hdr[2], provider1, plen); if(!Curl_checkheaders(data, STRCONST("Host"))) { char *fullhost; @@ -336,6 +336,7 @@ fail: /* try to parse a payload hash from the content-sha256 header */ static char *parse_content_sha_hdr(struct Curl_easy *data, const char *provider1, + size_t plen, size_t *value_len) { char key[CONTENT_SHA256_KEY_LEN]; @@ -343,7 +344,8 @@ static char *parse_content_sha_hdr(struct Curl_easy *data, char *value; size_t len; - key_len = msnprintf(key, sizeof(key), "x-%s-content-sha256", provider1); + key_len = msnprintf(key, sizeof(key), "x-%.*s-content-sha256", + (int)plen, provider1); value = Curl_checkheaders(data, key, key_len); if(!value) @@ -389,6 +391,7 @@ static CURLcode calc_payload_hash(struct Curl_easy *data, static CURLcode calc_s3_payload_hash(struct Curl_easy *data, Curl_HttpReq httpreq, char *provider1, + size_t plen, unsigned char *sha_hash, char *sha_hex, char *header) { @@ -415,7 +418,7 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, /* format the required content-sha256 header */ msnprintf(header, CONTENT_SHA256_HDR_LEN, - "x-%s-content-sha256: %s", provider1, sha_hex); + "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex); ret = CURLE_OK; fail: @@ -432,6 +435,8 @@ static int compare_func(const void *a, const void *b) const struct pair *aa = a; const struct pair *bb = b; /* If one element is empty, the other is always sorted higher */ + if(aa->len == 0 && bb->len == 0) + return 0; if(aa->len == 0) return -1; if(bb->len == 0) @@ -571,12 +576,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) CURLcode result = CURLE_OUT_OF_MEMORY; struct connectdata *conn = data->conn; size_t len; - const char *arg; - char provider0[MAX_SIGV4_LEN + 1]=""; - char provider1[MAX_SIGV4_LEN + 1]=""; - char region[MAX_SIGV4_LEN + 1]=""; - char service[MAX_SIGV4_LEN + 1]=""; - bool sign_as_s3 = FALSE; + char *line; + struct Curl_str provider0; + struct Curl_str provider1; + struct Curl_str region = { NULL, 0}; + struct Curl_str service = { NULL, 0}; const char *hostname = conn->host.name; time_t clock; struct tm tm; @@ -625,27 +629,31 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) * AWS is the default because most of non-amazon providers * are still using aws:amz as a prefix. */ - arg = data->set.str[STRING_AWS_SIGV4] ? - data->set.str[STRING_AWS_SIGV4] : "aws:amz"; + line = data->set.str[STRING_AWS_SIGV4] ? + data->set.str[STRING_AWS_SIGV4] : (char *)"aws:amz"; - /* provider1[:provider2[:region[:service]]] + /* provider0[:provider1[:region[:service]]] No string can be longer than N bytes of non-whitespace */ - (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]" - ":%" MAX_SIGV4_LEN_TXT "[^:]" - ":%" MAX_SIGV4_LEN_TXT "[^:]" - ":%" MAX_SIGV4_LEN_TXT "s", - provider0, provider1, region, service); - if(!provider0[0]) { + if(Curl_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) { failf(data, "first aws-sigv4 provider cannot be empty"); result = CURLE_BAD_FUNCTION_ARGUMENT; goto fail; } - else if(!provider1[0]) - strcpy(provider1, provider0); + if(Curl_str_single(&line, ':') || + Curl_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) { + provider1.str = provider0.str; + provider1.len = provider0.len; + } + else if(Curl_str_single(&line, ':') || + Curl_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') || + Curl_str_single(&line, ':') || + Curl_str_until(&line, &service, MAX_SIGV4_LEN, ':')) { + /* nothing to do */ + } - if(!service[0]) { + if(!service.len) { char *hostdot = strchr(hostname, '.'); if(!hostdot) { failf(data, "aws-sigv4: service missing in parameters and hostname"); @@ -658,12 +666,13 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) result = CURLE_URL_MALFORMAT; goto fail; } - memcpy(service, hostname, len); - service[len] = '\0'; + service.str = (char *)hostname; + service.len = len; - infof(data, "aws_sigv4: picked service %s from host", service); + infof(data, "aws_sigv4: picked service %.*s from host", + (int)service.len, service.str); - if(!region[0]) { + if(!region.len) { const char *reg = hostdot + 1; const char *hostreg = strchr(reg, '.'); if(!hostreg) { @@ -677,25 +686,29 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) result = CURLE_URL_MALFORMAT; goto fail; } - memcpy(region, reg, len); - region[len] = '\0'; - infof(data, "aws_sigv4: picked region %s from host", region); + region.str = (char *)reg; + region.len = len; + infof(data, "aws_sigv4: picked region %.*s from host", + (int)region.len, region.str); } } Curl_http_method(data, conn, &method, &httpreq); - /* AWS S3 requires a x-amz-content-sha256 header, and supports special - * values like UNSIGNED-PAYLOAD */ - sign_as_s3 = (strcasecompare(provider0, "aws") && - strcasecompare(service, "s3")); - - payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len); + payload_hash = parse_content_sha_hdr(data, provider1.str, provider1.len, + &payload_hash_len); if(!payload_hash) { + /* AWS S3 requires a x-amz-content-sha256 header, and supports special + * values like UNSIGNED-PAYLOAD */ + bool sign_as_s3 = ((provider0.len == 3) && + strncasecompare(provider0.str, "aws", 3)) && + ((service.len == 2) && strncasecompare(service.str, "s3", 2)); + if(sign_as_s3) - result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, - sha_hex, content_sha256_hdr); + result = calc_s3_payload_hash(data, httpreq, + provider1.str, provider1.len, + sha_hash, sha_hex, content_sha256_hdr); else result = calc_payload_hash(data, sha_hash, sha_hex); if(result) @@ -726,7 +739,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) goto fail; } - result = make_headers(data, hostname, timestamp, provider1, + result = make_headers(data, hostname, timestamp, + provider1.str, provider1.len, &date_header, content_sha256_hdr, &canonical_headers, &signed_headers); if(result) @@ -771,14 +785,18 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) DEBUGF(infof(data, "Canonical request: %s", canonical_request)); - /* provider 0 lowercase */ - Curl_strntolower(provider0, provider0, strlen(provider0)); - request_type = aprintf("%s4_request", provider0); + request_type = aprintf("%.*s4_request", (int)provider0.len, provider0.str); if(!request_type) goto fail; - credential_scope = aprintf("%s/%s/%s/%s", - date, region, service, request_type); + /* provider0 is lowercased *after* aprintf() so that the buffer can be + written to */ + Curl_strntolower(request_type, request_type, provider0.len); + + credential_scope = aprintf("%s/%.*s/%.*s/%s", + date, (int)region.len, region.str, + (int)service.len, service.str, + request_type); if(!credential_scope) goto fail; @@ -788,42 +806,41 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) sha256_to_hex(sha_hex, sha_hash); - /* provider 0 uppercase */ - Curl_strntoupper(provider0, provider0, strlen(provider0)); - /* * Google allows using RSA key instead of HMAC, so this code might change * in the future. For now we only support HMAC. */ - str_to_sign = aprintf("%s4-HMAC-SHA256\n" /* Algorithm */ + str_to_sign = aprintf("%.*s4-HMAC-SHA256\n" /* Algorithm */ "%s\n" /* RequestDateTime */ "%s\n" /* CredentialScope */ "%s", /* HashedCanonicalRequest in hex */ - provider0, + (int)provider0.len, provider0.str, timestamp, credential_scope, sha_hex); - if(!str_to_sign) { + if(!str_to_sign) goto fail; - } - /* provider 0 uppercase */ - secret = aprintf("%s4%s", provider0, + /* make provider0 part done uppercase */ + Curl_strntoupper(str_to_sign, provider0.str, provider0.len); + + secret = aprintf("%.*s4%s", (int)provider0.len, provider0.str, data->state.aptr.passwd ? data->state.aptr.passwd : ""); if(!secret) goto fail; + /* make provider0 part done uppercase */ + Curl_strntoupper(secret, provider0.str, provider0.len); HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0); - HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1); - HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0); + HMAC_SHA256(sign0, sizeof(sign0), region.str, region.len, sign1); + HMAC_SHA256(sign1, sizeof(sign1), service.str, service.len, sign0); HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); sha256_to_hex(sha_hex, sign0); - /* provider 0 uppercase */ - auth_headers = aprintf("Authorization: %s4-HMAC-SHA256 " + auth_headers = aprintf("Authorization: %.*s4-HMAC-SHA256 " "Credential=%s/%s, " "SignedHeaders=%s, " "Signature=%s\r\n" @@ -834,7 +851,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) */ "%s" "%s", /* optional sha256 header includes \r\n */ - provider0, + (int)provider0.len, provider0.str, user, credential_scope, Curl_dyn_ptr(&signed_headers), @@ -844,6 +861,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) if(!auth_headers) { goto fail; } + /* provider 0 uppercase */ + Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], + provider0.str, provider0.len); Curl_safefree(data->state.aptr.userpwd); data->state.aptr.userpwd = auth_headers; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 5d76bdd..f031d0a 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -27,6 +27,7 @@ #if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) #include "urldata.h" +#include "cfilters.h" #include "sendf.h" #include "http_negotiate.h" #include "vauth/vauth.h" @@ -109,7 +110,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif /* Check if the connection is using SSL and get the channel binding data */ #if defined(USE_SSL) && defined(HAVE_GSSAPI) - if(conn->handler->flags & PROTOPT_SSL) { + if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { Curl_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1); result = Curl_ssl_get_channel_binding( data, FIRSTSOCKET, &neg_ctx->channel_binding_data); diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 49230bc..ab6f1dd 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -252,6 +252,12 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) break; case NTLMSTATE_LAST: + /* since this is a little artificial in that this is used without any + outgoing auth headers being set, we need to set the bit by force */ + if(proxy) + data->info.proxyauthpicked = CURLAUTH_NTLM; + else + data->info.httpauthpicked = CURLAUTH_NTLM; Curl_safefree(*allocuserpwd); authp->done = TRUE; break; diff --git a/lib/http_proxy.c b/lib/http_proxy.c index b1dbe98..3a2e9f2 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -29,9 +29,6 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) #include -#ifdef USE_HYPER -#include -#endif #include "sendf.h" #include "http.h" #include "url.h" @@ -59,7 +56,7 @@ static bool hd_name_eq(const char *n1, size_t n1len, } static CURLcode dynhds_add_custom(struct Curl_easy *data, - bool is_connect, + bool is_connect, int httpversion, struct dynhds *hds) { struct connectdata *conn = data->conn; @@ -172,9 +169,9 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, Connection: */ hd_name_eq(name, namelen, STRCONST("Connection:"))) ; - else if((conn->httpversion >= 20) && + else if((httpversion >= 20) && hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:"))) - /* HTTP/2 does not support chunked requests */ + /* HTTP/2 and HTTP/3 do not support chunked requests */ ; else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) || hd_name_eq(name, namelen, STRCONST("Cookie:"))) && @@ -224,11 +221,18 @@ CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, return CURLE_OK; } +struct cf_proxy_ctx { + /* the protocol specific sub-filter we install during connect */ + struct Curl_cfilter *cf_protocol; + int httpversion; /* HTTP version used to CONNECT */ +}; + CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf, struct Curl_easy *data, int http_version_major) { + struct cf_proxy_ctx *ctx = cf->ctx; const char *hostname = NULL; char *authority = NULL; int port; @@ -289,7 +293,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - result = dynhds_add_custom(data, TRUE, &req->headers); + result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers); out: if(result && req) { @@ -301,12 +305,6 @@ out: return result; } - -struct cf_proxy_ctx { - /* the protocol specific sub-filter we install during connect */ - struct Curl_cfilter *cf_protocol; -}; - static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done) @@ -328,6 +326,7 @@ connect_sub: *done = FALSE; if(!ctx->cf_protocol) { struct Curl_cfilter *cf_protocol = NULL; + int httpversion = 0; int alpn = Curl_conn_cf_is_ssl(cf->next) ? cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1; @@ -343,6 +342,7 @@ connect_sub: if(result) goto out; cf_protocol = cf->next; + httpversion = (alpn == CURL_HTTP_VERSION_1_0) ? 10 : 11; break; #ifdef USE_NGHTTP2 case CURL_HTTP_VERSION_2: @@ -352,6 +352,7 @@ connect_sub: if(result) goto out; cf_protocol = cf->next; + httpversion = 20; break; #endif default: @@ -361,6 +362,7 @@ connect_sub: } ctx->cf_protocol = cf_protocol; + ctx->httpversion = httpversion; /* after we installed the filter "below" us, we call connect * on out sub-chain again. */ diff --git a/lib/httpsrr.c b/lib/httpsrr.c new file mode 100644 index 0000000..9884b92 --- /dev/null +++ b/lib/httpsrr.c @@ -0,0 +1,167 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_HTTPSRR + +#include "urldata.h" +#include "curl_addrinfo.h" +#include "httpsrr.h" +#include "connect.h" +#include "sendf.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len, + unsigned char *alpns) +{ + /* + * spec here is as per RFC 9460, section-7.1.1 + * encoding is a concatenated list of strings each preceded by a one + * octet length + * output is comma-sep list of the strings + * implementations may or may not handle quoting of comma within + * string values, so we might see a comma within the wire format + * version of a string, in which case we will precede that by a + * backslash - same goes for a backslash character, and of course + * we need to use two backslashes in strings when we mean one;-) + */ + struct dynbuf dval; + int idnum = 0; + + Curl_dyn_init(&dval, DYN_DOH_RESPONSE); + while(len > 0) { + size_t tlen = (size_t) *cp++; + size_t i; + enum alpnid id; + len--; + if(tlen > len) + goto err; + /* add escape char if needed, clunky but easier to read */ + for(i = 0; i != tlen; i++) { + if('\\' == *cp || ',' == *cp) { + if(Curl_dyn_addn(&dval, "\\", 1)) + goto err; + } + if(Curl_dyn_addn(&dval, cp++, 1)) + goto err; + } + len -= tlen; + + /* we only store ALPN ids we know about */ + id = Curl_alpn2alpnid(Curl_dyn_ptr(&dval), Curl_dyn_len(&dval)); + if(id != ALPN_none) { + if(idnum == MAX_HTTPSRR_ALPNS) + break; + alpns[idnum++] = (unsigned char)id; + } + Curl_dyn_reset(&dval); + } + Curl_dyn_free(&dval); + if(idnum < MAX_HTTPSRR_ALPNS) + alpns[idnum] = ALPN_none; /* terminate the list */ + return CURLE_OK; +err: + Curl_dyn_free(&dval); + return CURLE_BAD_CONTENT_ENCODING; +} + +#ifdef USE_ARES + +static void httpsrr_opt(struct Curl_easy *data, + const ares_dns_rr_t *rr, + ares_dns_rr_key_t key, size_t idx) +{ + size_t len = 0; + const unsigned char *val = NULL; + unsigned short code; + struct thread_data *res = data->state.async.tdata; + struct Curl_https_rrinfo *hi = &res->hinfo; + code = ares_dns_rr_get_opt(rr, key, idx, &val, &len); + + switch(code) { + case HTTPS_RR_CODE_ALPN: /* str_list */ + Curl_httpsrr_decode_alpn(val, len, hi->alpns); + infof(data, "HTTPS RR ALPN: %u %u %u %u", + hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]); + break; + case HTTPS_RR_CODE_NO_DEF_ALPN: + infof(data, "HTTPS RR no-def-alpn"); + break; + case HTTPS_RR_CODE_IPV4: /* addr4 list */ + infof(data, "HTTPS RR IPv4"); + break; + case HTTPS_RR_CODE_ECH: + infof(data, "HTTPS RR ECH"); + break; + case HTTPS_RR_CODE_IPV6: /* addr6 list */ + infof(data, "HTTPS RR IPv6"); + break; + case HTTPS_RR_CODE_PORT: + infof(data, "HTTPS RR port"); + break; + default: + infof(data, "HTTPS RR unknown code"); + break; + } +} + +void Curl_dnsrec_done_cb(void *arg, ares_status_t status, + size_t timeouts, + const ares_dns_record_t *dnsrec) +{ + struct Curl_easy *data = arg; + size_t i; +#ifdef CURLRES_ARES + struct thread_data *res = data->state.async.tdata; + + res->num_pending--; +#endif + (void)timeouts; + if((ARES_SUCCESS != status) || !dnsrec) + return; + + for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) { + size_t opt; + const ares_dns_rr_t *rr = + ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i); + if(ares_dns_rr_get_type(rr) != ARES_REC_TYPE_HTTPS) + continue; + /* When SvcPriority is 0, the SVCB record is in AliasMode. Otherwise, it + is in ServiceMode */ + infof(data, "HTTPS RR priority: %u", + ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY)); + for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS); + opt++) + httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt); + } +} + +#endif /* USE_ARES */ + +#endif /* USE_HTTPSRR */ diff --git a/lib/httpsrr.h b/lib/httpsrr.h new file mode 100644 index 0000000..ade2126 --- /dev/null +++ b/lib/httpsrr.h @@ -0,0 +1,76 @@ +#ifndef HEADER_CURL_HTTPSRR_H +#define HEADER_CURL_HTTPSRR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_ARES +#include +#endif + +#ifdef USE_HTTPSRR + +#define CURL_MAXLEN_host_name 253 +#define MAX_HTTPSRR_ALPNS 4 + +struct Curl_https_rrinfo { + /* + * Fields from HTTPS RR. The only mandatory fields are priority and target. + * See https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2 + */ + char *target; + unsigned char *ipv4hints; /* keytag = 4 */ + size_t ipv4hints_len; + unsigned char *echconfiglist; /* keytag = 5 */ + size_t echconfiglist_len; + unsigned char *ipv6hints; /* keytag = 6 */ + size_t ipv6hints_len; + unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */ + /* store parsed alpnid entries in the array, end with ALPN_none */ + int port; /* -1 means not set */ + uint16_t priority; + bool no_def_alpn; /* keytag = 2 */ +}; +#endif + +/* + * Code points for DNS wire format SvcParams as per RFC 9460 + */ +#define HTTPS_RR_CODE_ALPN 0x01 +#define HTTPS_RR_CODE_NO_DEF_ALPN 0x02 +#define HTTPS_RR_CODE_PORT 0x03 +#define HTTPS_RR_CODE_IPV4 0x04 +#define HTTPS_RR_CODE_ECH 0x05 +#define HTTPS_RR_CODE_IPV6 0x06 + +CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len, + unsigned char *alpns); + +#if defined(USE_ARES) && defined(USE_HTTPSRR) +void Curl_dnsrec_done_cb(void *arg, ares_status_t status, + size_t timeouts, + const ares_dns_record_t *dnsrec); +#endif +#endif /* HEADER_CURL_HTTPSRR_H */ diff --git a/lib/imap.c b/lib/imap.c index e424cdb..e5ee401 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -134,6 +134,7 @@ const struct Curl_handler Curl_handler_imap = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_IMAP, /* defport */ CURLPROTO_IMAP, /* protocol */ CURLPROTO_IMAP, /* family */ @@ -164,6 +165,7 @@ const struct Curl_handler Curl_handler_imaps = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_IMAPS, /* defport */ CURLPROTO_IMAPS, /* protocol */ CURLPROTO_IMAP, /* family */ @@ -1390,7 +1392,7 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; struct imap_conn *imapc = &conn->proto.imapc; - if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) { + if(Curl_conn_is_ssl(conn, FIRSTSOCKET) && !imapc->ssldone) { bool ssldone = FALSE; result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); imapc->ssldone = ssldone; diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c index a2812cf..bce0ed7 100644 --- a/lib/inet_ntop.c +++ b/lib/inet_ntop.c @@ -75,7 +75,7 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size) len = strlen(tmp); if(len == 0 || len >= size) { errno = ENOSPC; - return (NULL); + return NULL; } strcpy(dst, tmp); return dst; @@ -154,7 +154,7 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) { errno = ENOSPC; - return (NULL); + return NULL; } tp += strlen(tp); break; @@ -172,7 +172,7 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) */ if((size_t)(tp - tmp) > size) { errno = ENOSPC; - return (NULL); + return NULL; } strcpy(dst, tmp); return dst; diff --git a/lib/inet_ntop.h b/lib/inet_ntop.h index f592f25..3b90ed3 100644 --- a/lib/inet_ntop.h +++ b/lib/inet_ntop.h @@ -33,8 +33,15 @@ char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); #include #endif #ifdef _WIN32 +#if defined(_MSC_VER) && (_MSC_VER <= 1900) +#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af, (void *)addr, buf, size) +#else +#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af, addr, buf, size) +#endif +#elif defined(__AMIGA__) #define Curl_inet_ntop(af,addr,buf,size) \ - inet_ntop(af, addr, buf, size) + (char *)inet_ntop(af, (void *)addr, (unsigned char *)buf, \ + (curl_socklen_t)(size)) #else #define Curl_inet_ntop(af,addr,buf,size) \ inet_ntop(af, addr, buf, (curl_socklen_t)(size)) diff --git a/lib/inet_pton.c b/lib/inet_pton.c index 97e6f80..d0c04db 100644 --- a/lib/inet_pton.c +++ b/lib/inet_pton.c @@ -76,12 +76,12 @@ Curl_inet_pton(int af, const char *src, void *dst) { switch(af) { case AF_INET: - return (inet_pton4(src, (unsigned char *)dst)); + return inet_pton4(src, (unsigned char *)dst); case AF_INET6: - return (inet_pton6(src, (unsigned char *)dst)); + return inet_pton6(src, (unsigned char *)dst); default: errno = EAFNOSUPPORT; - return (-1); + return -1; } /* NOTREACHED */ } @@ -116,29 +116,29 @@ inet_pton4(const char *src, unsigned char *dst) (unsigned int)(pch - digits); if(saw_digit && *tp == 0) - return (0); + return 0; if(val > 255) - return (0); + return 0; *tp = (unsigned char)val; if(!saw_digit) { if(++octets > 4) - return (0); + return 0; saw_digit = 1; } } else if(ch == '.' && saw_digit) { if(octets == 4) - return (0); + return 0; *++tp = 0; saw_digit = 0; } else - return (0); + return 0; } if(octets < 4) - return (0); + return 0; memcpy(dst, tmp, INADDRSZ); - return (1); + return 1; } /* int @@ -170,7 +170,7 @@ inet_pton6(const char *src, unsigned char *dst) /* Leading :: requires some special handling. */ if(*src == ':') if(*++src != ':') - return (0); + return 0; curtok = src; saw_xdigit = 0; val = 0; @@ -185,19 +185,19 @@ inet_pton6(const char *src, unsigned char *dst) val <<= 4; val |= (pch - xdigits); if(++saw_xdigit > 4) - return (0); + return 0; continue; } if(ch == ':') { curtok = src; if(!saw_xdigit) { if(colonp) - return (0); + return 0; colonp = tp; continue; } if(tp + INT16SZ > endp) - return (0); + return 0; *tp++ = (unsigned char) ((val >> 8) & 0xff); *tp++ = (unsigned char) (val & 0xff); saw_xdigit = 0; @@ -210,11 +210,11 @@ inet_pton6(const char *src, unsigned char *dst) saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } - return (0); + return 0; } if(saw_xdigit) { if(tp + INT16SZ > endp) - return (0); + return 0; *tp++ = (unsigned char) ((val >> 8) & 0xff); *tp++ = (unsigned char) (val & 0xff); } @@ -227,7 +227,7 @@ inet_pton6(const char *src, unsigned char *dst) ssize_t i; if(tp == endp) - return (0); + return 0; for(i = 1; i <= n; i++) { *(endp - i) = *(colonp + n - i); *(colonp + n - i) = 0; @@ -235,9 +235,9 @@ inet_pton6(const char *src, unsigned char *dst) tp = endp; } if(tp != endp) - return (0); + return 0; memcpy(dst, tmp, IN6ADDRSZ); - return (1); + return 1; } #endif /* HAVE_INET_PTON */ diff --git a/lib/inet_pton.h b/lib/inet_pton.h index f8562fa..50bce61 100644 --- a/lib/inet_pton.h +++ b/lib/inet_pton.h @@ -32,7 +32,11 @@ int Curl_inet_pton(int, const char *, void *); #ifdef HAVE_ARPA_INET_H #include #endif +#if defined(__AMIGA__) +#define Curl_inet_pton(x,y,z) inet_pton(x,(unsigned char *)y,z) +#else #define Curl_inet_pton(x,y,z) inet_pton(x,y,z) #endif +#endif #endif /* HEADER_CURL_INET_PTON_H */ diff --git a/lib/krb5.c b/lib/krb5.c index e310a1b..4faa263 100644 --- a/lib/krb5.c +++ b/lib/krb5.c @@ -63,6 +63,11 @@ #include "curl_memory.h" #include "memdebug.h" +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, const char *cmd) { @@ -924,4 +929,8 @@ Curl_sec_end(struct connectdata *conn) conn->mech = NULL; } +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + #endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */ diff --git a/lib/ldap.c b/lib/ldap.c index 2cbdb9c..0fa304b 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -26,6 +26,11 @@ #if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP) +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + /* * Notice that USE_OPENLDAP is only a source code selection switch. When * libcurl is built with USE_OPENLDAP defined the libcurl source code that @@ -52,7 +57,7 @@ #ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ # ifdef _MSC_VER # pragma warning(push) -# pragma warning(disable: 4201) +# pragma warning(disable:4201) # endif # include /* for [P]UNICODE_STRING */ # ifdef _MSC_VER @@ -78,6 +83,7 @@ #include "urldata.h" #include +#include "cfilters.h" #include "sendf.h" #include "escape.h" #include "progress.h" @@ -148,7 +154,7 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp); #define ldap_err2string ldap_err2stringA #endif -#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1600) +#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1700) /* Workaround for warning: 'type cast' : conversion from 'int' to 'void *' of greater size */ #undef LDAP_OPT_ON @@ -181,6 +187,7 @@ const struct Curl_handler Curl_handler_ldap = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_LDAP, /* defport */ CURLPROTO_LDAP, /* protocol */ CURLPROTO_LDAP, /* family */ @@ -210,6 +217,7 @@ const struct Curl_handler Curl_handler_ldaps = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_LDAPS, /* defport */ CURLPROTO_LDAPS, /* protocol */ CURLPROTO_LDAP, /* family */ @@ -346,7 +354,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) } /* Get the URL scheme (either ldap or ldaps) */ - if(conn->given->flags & PROTOPT_SSL) + if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) ldap_ssl = 1; infof(data, "LDAP local: trying to establish %s connection", ldap_ssl ? "encrypted" : "cleartext"); @@ -800,7 +808,7 @@ static int str2scope(const char *p) return LDAP_SCOPE_SUBTREE; if(strcasecompare(p, "subtree")) return LDAP_SCOPE_SUBTREE; - return (-1); + return -1; } /* @@ -1082,7 +1090,7 @@ static int _ldap_url_parse(struct Curl_easy *data, ludp = NULL; } *ludpp = ludp; - return (rc); + return rc; } static void _ldap_free_urldesc(LDAPURLDesc *ludp) @@ -1113,4 +1121,9 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp) free(ludp); } #endif /* !HAVE_LDAP_URL_PARSE */ + +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + #endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */ diff --git a/lib/llist.c b/lib/llist.c index e5c65fb..a2c199b 100644 --- a/lib/llist.c +++ b/lib/llist.c @@ -134,40 +134,38 @@ Curl_llist_append(struct Curl_llist *list, const void *p, Curl_llist_insert_next(list, list->_tail, p, ne); } -/* - * @unittest: 1300 - */ -void -Curl_node_uremove(struct Curl_llist_node *e, void *user) +void *Curl_node_take_elem(struct Curl_llist_node *e) { void *ptr; struct Curl_llist *list; if(!e) - return; + return NULL; 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) { + if(e == list->_head) { + list->_head = e->_next; - if(!list->_head) - list->_tail = NULL; - else - e->_next->_prev = NULL; - } - else { - if(e->_prev) - e->_prev->_next = e->_next; + if(!list->_head) + list->_tail = NULL; + else + e->_next->_prev = NULL; + } + else { + if(e->_prev) + e->_prev->_next = e->_next; - if(!e->_next) - list->_tail = e->_prev; - else - e->_next->_prev = e->_prev; + if(!e->_next) + list->_tail = e->_prev; + else + e->_next->_prev = e->_prev; + } + --list->_size; } - ptr = e->_ptr; e->_list = NULL; @@ -178,11 +176,27 @@ Curl_node_uremove(struct Curl_llist_node *e, void *user) e->_init = NODEREM; /* specific pattern on remove - not zero */ #endif - --list->_size; + return ptr; +} - /* call the dtor() last for when it actually frees the 'e' memory itself */ - if(list->_dtor) - list->_dtor(user, ptr); +/* + * @unittest: 1300 + */ +void +Curl_node_uremove(struct Curl_llist_node *e, void *user) +{ + struct Curl_llist *list; + void *ptr; + if(!e) + return; + + list = e->_list; + DEBUGASSERT(list); + if(list) { + ptr = Curl_node_take_elem(e); + if(list->_dtor) + list->_dtor(user, ptr); + } } void Curl_node_remove(struct Curl_llist_node *e) diff --git a/lib/llist.h b/lib/llist.h index 2658186..597c0e0 100644 --- a/lib/llist.h +++ b/lib/llist.h @@ -75,6 +75,10 @@ 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); +/* Remove the node from the list and return the custom data + * from a Curl_llist_node. Will NOT incoke a registered `dtor`. */ +void *Curl_node_take_elem(struct Curl_llist_node *); + /* Curl_node_next() returns the next element in a list from a given Curl_llist_node */ struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n); diff --git a/lib/macos.c b/lib/macos.c index e4662be..daf2ab9 100644 --- a/lib/macos.c +++ b/lib/macos.c @@ -37,7 +37,7 @@ 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 + * 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 diff --git a/lib/memdebug.c b/lib/memdebug.c index 02612c2..9c284ed 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -153,7 +153,7 @@ ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize, source, line, wantedsize, mem ? (void *)mem->mem : (void *)0); - return (mem ? mem->mem : NULL); + return mem ? mem->mem : NULL; } ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, @@ -181,7 +181,7 @@ ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, source, line, wanted_elements, wanted_size, mem ? (void *)mem->mem : (void *)0); - return (mem ? mem->mem : NULL); + return mem ? mem->mem : NULL; } ALLOC_FUNC char *curl_dbg_strdup(const char *str, diff --git a/lib/mime.c b/lib/mime.c index 21c40b0..d448891 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -1561,6 +1561,14 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part, } } + /* If subparts have already been used as a top-level MIMEPOST, + they might not be positioned at start. Rewind them now, as + a future check while rewinding the parent may cause this + content to be skipped. */ + if(mime_subparts_seek(subparts, (curl_off_t) 0, SEEK_SET) != + CURL_SEEKFUNC_OK) + return CURLE_SEND_FAIL_REWIND; + subparts->parent = part; /* Subparts are processed internally: no read callback. */ part->seekfunc = mime_subparts_seek; @@ -2171,7 +2179,7 @@ static bool cr_mime_is_paused(struct Curl_easy *data, { struct cr_mime_ctx *ctx = reader->ctx; (void)data; - return (ctx->part && ctx->part->lastreadstatus == CURL_READFUNC_PAUSE); + return ctx->part && ctx->part->lastreadstatus == CURL_READFUNC_PAUSE; } static const struct Curl_crtype cr_mime = { diff --git a/lib/mprintf.c b/lib/mprintf.c index 35e40e3..8bc9054 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -37,14 +37,12 @@ #ifdef HAVE_LONGLONG # define LONG_LONG_TYPE long long # define HAVE_LONG_LONG_TYPE +#elif defined(_MSC_VER) +# define LONG_LONG_TYPE __int64 +# define HAVE_LONG_LONG_TYPE #else -# if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) -# define LONG_LONG_TYPE __int64 -# define HAVE_LONG_LONG_TYPE -# else -# undef LONG_LONG_TYPE -# undef HAVE_LONG_LONG_TYPE -# endif +# undef LONG_LONG_TYPE +# undef HAVE_LONG_LONG_TYPE #endif /* @@ -680,12 +678,12 @@ static int formatf( struct outsegment output[MAX_SEGMENTS]; struct va_input input[MAX_PARAMETERS]; - char work[BUFFSIZE]; + char work[BUFFSIZE + 2]; /* 'workend' points to the final buffer byte position, but with an extra byte as margin to avoid the (FALSE?) warning Coverity gives us otherwise */ - char *workend = &work[sizeof(work) - 2]; + char *workend = &work[BUFFSIZE - 2]; /* Parse the format string */ if(parsefmt(format, output, input, &ocount, &icount, ap_save)) @@ -968,8 +966,8 @@ number: if(width >= 0) { size_t dlen; - if(width >= (int)sizeof(work)) - width = sizeof(work)-1; + if(width >= BUFFSIZE) + width = BUFFSIZE - 1; /* RECURSIVE USAGE */ dlen = (size_t)curl_msnprintf(fptr, left, "%d", width); fptr += dlen; @@ -978,17 +976,19 @@ number: if(prec >= 0) { /* for each digit in the integer part, we can have one less precision */ - size_t maxprec = sizeof(work) - 2; + int maxprec = BUFFSIZE - 1; double val = iptr->val.dnum; + if(prec > maxprec) + prec = maxprec - 1; if(width > 0 && prec <= width) - maxprec -= (size_t)width; + maxprec -= width; while(val >= 10.0) { val /= 10; maxprec--; } - if(prec > (int)maxprec) - prec = (int)maxprec-1; + if(prec > maxprec) + prec = maxprec - 1; if(prec < 0) prec = 0; /* RECURSIVE USAGE */ @@ -1014,14 +1014,19 @@ number: /* NOTE NOTE NOTE!! Not all sprintf implementations return number of output characters */ #ifdef HAVE_SNPRINTF - (snprintf)(work, sizeof(work), formatbuf, iptr->val.dnum); + (snprintf)(work, BUFFSIZE, formatbuf, iptr->val.dnum); /* NOLINT */ +#ifdef _WIN32 + /* Old versions of the Windows CRT do not terminate the snprintf output + buffer if it reaches the max size so we do that here. */ + work[BUFFSIZE - 1] = 0; +#endif #else (sprintf)(work, formatbuf, iptr->val.dnum); #endif #ifdef __clang__ #pragma clang diagnostic pop #endif - DEBUGASSERT(strlen(work) <= sizeof(work)); + DEBUGASSERT(strlen(work) < BUFFSIZE); for(fptr = work; *fptr; fptr++) OUTCHAR(*fptr); break; diff --git a/lib/mqtt.c b/lib/mqtt.c index 69eaf34..fe242c7 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -92,6 +92,7 @@ const struct Curl_handler Curl_handler_mqtt = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_MQTT, /* defport */ CURLPROTO_MQTT, /* protocol */ CURLPROTO_MQTT, /* family */ diff --git a/lib/multi.c b/lib/multi.c index 1851dc7..d4dd4a0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -46,6 +46,7 @@ #include "multihandle.h" #include "sigpipe.h" #include "vtls/vtls.h" +#include "vtls/vtls_scache.h" #include "http_proxy.h" #include "http2.h" #include "socketpair.h" @@ -59,7 +60,7 @@ /* CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every - CURL handle takes 45-50 K memory, therefore this 3K are not significant. + curl handle takes 6K memory, therefore this 3K are not significant. */ #ifndef CURL_SOCKET_HASH_TABLE_SIZE #define CURL_SOCKET_HASH_TABLE_SIZE 911 @@ -73,6 +74,10 @@ #define CURL_DNS_HASH_SIZE 71 #endif +#ifndef CURL_TLS_SESSION_SIZE +#define CURL_TLS_SESSION_SIZE 25 +#endif + #define CURL_MULTI_HANDLE 0x000bab1e #ifdef DEBUGBUILD @@ -395,9 +400,10 @@ static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg) Curl_llist_append(&multi->msglist, msg, &msg->list); } -struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ +struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ size_t chashsize, /* connection hash */ - size_t dnssize) /* dns hash */ + size_t dnssize, /* dns hash */ + size_t sesssize) /* TLS session cache */ { struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); @@ -414,7 +420,10 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ Curl_hash_str, Curl_str_key_compare, ph_freeentry); if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect, - multi, NULL, chashsize)) + multi, NULL, chashsize)) + goto error; + + if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache)) goto error; Curl_llist_init(&multi->msglist, NULL); @@ -447,6 +456,7 @@ error: Curl_hash_destroy(&multi->proto_hash); Curl_hash_destroy(&multi->hostcache); Curl_cpool_destroy(&multi->cpool); + Curl_ssl_scache_destroy(multi->ssl_scache); free(multi); return NULL; } @@ -455,7 +465,8 @@ CURLM *curl_multi_init(void) { return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE, CURL_CONNECTION_HASH_SIZE, - CURL_DNS_HASH_SIZE); + CURL_DNS_HASH_SIZE, + CURL_TLS_SESSION_SIZE); } #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -787,7 +798,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) return CURLM_BAD_HANDLE; /* Verify that we got a somewhat good easy handle too */ - if(!GOOD_EASY_HANDLE(data) || !multi->num_easy) + if(!GOOD_EASY_HANDLE(data)) return CURLM_BAD_EASY_HANDLE; /* Prevent users from trying to remove same easy handle more than once */ @@ -798,6 +809,11 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) if(data->multi != multi) return CURLM_BAD_EASY_HANDLE; + if(!multi->num_easy) { + DEBUGASSERT(0); + return CURLM_INTERNAL_ERROR; + } + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; @@ -917,7 +933,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) /* Return TRUE if the application asked for multiplexing */ bool Curl_multiplex_wanted(const struct Curl_multi *multi) { - return (multi && (multi->multiplexing)); + return multi && multi->multiplexing; } /* @@ -930,7 +946,6 @@ void Curl_detach_connection(struct Curl_easy *data) { struct connectdata *conn = data->conn; if(conn) { - Curl_conn_ev_data_detach(conn, data); Curl_node_remove(&data->conn_queue); } data->conn = NULL; @@ -951,7 +966,6 @@ void Curl_attach_connection(struct Curl_easy *data, Curl_llist_append(&conn->easyq, data, &data->conn_queue); if(conn->handler && conn->handler->attach) conn->handler->attach(data, conn); - Curl_conn_ev_data_attach(conn, data); } static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks) @@ -1152,6 +1166,7 @@ CURLMcode curl_multi_fdset(CURLM *m, int this_max_fd = -1; struct Curl_llist_node *e; struct Curl_multi *multi = m; + unsigned int i; (void)exc_fd_set; /* not used */ if(!GOOD_MULTI_HANDLE(multi)) @@ -1162,7 +1177,6 @@ CURLMcode curl_multi_fdset(CURLM *m, 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); @@ -1170,15 +1184,24 @@ CURLMcode curl_multi_fdset(CURLM *m, if(!FDSET_SOCK(data->last_poll.sockets[i])) /* pretend it does not exist */ continue; +#if defined(__DJGPP__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warith-conversion" +#endif 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 defined(__DJGPP__) +#pragma GCC diagnostic pop +#endif if((int)data->last_poll.sockets[i] > this_max_fd) this_max_fd = (int)data->last_poll.sockets[i]; } } + Curl_cpool_setfds(&multi->cpool, read_fd_set, write_fd_set, &this_max_fd); + *max_fd = this_max_fd; return CURLM_OK; @@ -1189,12 +1212,13 @@ CURLMcode curl_multi_waitfds(CURLM *m, unsigned int size, unsigned int *fd_count) { - struct curl_waitfds cwfds; + struct Curl_waitfds cwfds; CURLMcode result = CURLM_OK; struct Curl_llist_node *e; struct Curl_multi *multi = m; + unsigned int need = 0; - if(!ufds) + if(!ufds && (size || !fd_count)) return CURLM_BAD_FUNCTION_ARGUMENT; if(!GOOD_MULTI_HANDLE(multi)) @@ -1207,20 +1231,17 @@ CURLMcode curl_multi_waitfds(CURLM *m, 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; - } + need += Curl_waitfds_add_ps(&cwfds, &data->last_poll); } - if(Curl_cpool_add_waitfds(&multi->cpool, &cwfds)) { + need += Curl_cpool_add_waitfds(&multi->cpool, &cwfds); + + if(need != cwfds.n && ufds) { result = CURLM_OUT_OF_MEMORY; - goto out; } -out: if(fd_count) - *fd_count = cwfds.n; + *fd_count = need; return result; } @@ -1834,289 +1855,13 @@ static void multi_posttransfer(struct Curl_easy *data) * This function DOES NOT FREE the given url. */ static CURLcode multi_follow(struct Curl_easy *data, - char *newurl, /* the Location: string */ + const struct Curl_handler *handler, + const char *newurl, /* the Location: string */ followtype type) /* see transfer.h */ { -#ifdef CURL_DISABLE_HTTP - (void)data; - (void)newurl; - (void)type; - /* Location: following will not happen when HTTP is disabled */ + if(handler && handler->follow) + return handler->follow(data, newurl, type); return CURLE_TOO_MANY_REDIRECTS; -#else - - /* Location: redirect */ - bool disallowport = FALSE; - bool reachedmax = FALSE; - CURLUcode uc; - - DEBUGASSERT(type != FOLLOW_NONE); - - if(type != FOLLOW_FAKE) - data->state.requests++; /* count all real follows */ - if(type == FOLLOW_REDIR) { - if((data->set.maxredirs != -1) && - (data->state.followlocation >= data->set.maxredirs)) { - reachedmax = TRUE; - type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected - to URL */ - } - else { - data->state.followlocation++; /* count redirect-followings, including - auth reloads */ - - if(data->set.http_auto_referer) { - CURLU *u; - char *referer = NULL; - - /* We are asked to automatically set the previous URL as the referer - when we get the next URL. We pick the ->url field, which may or may - not be 100% correct */ - - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } - - /* Make a copy of the URL without credentials and fragment */ - u = curl_url(); - if(!u) - return CURLE_OUT_OF_MEMORY; - - uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_USER, NULL, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0); - if(!uc) - uc = curl_url_get(u, CURLUPART_URL, &referer, 0); - - curl_url_cleanup(u); - - if(uc || !referer) - return CURLE_OUT_OF_MEMORY; - - data->state.referer = referer; - data->state.referer_alloc = TRUE; /* yes, free this later */ - } - } - } - - if((type != FOLLOW_RETRY) && - (data->req.httpcode != 401) && (data->req.httpcode != 407) && - Curl_is_absolute_url(newurl, NULL, 0, FALSE)) { - /* If this is not redirect due to a 401 or 407 response and an absolute - URL: do not allow a custom port number */ - disallowport = TRUE; - } - - DEBUGASSERT(data->state.uh); - uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int) - ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : - ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) | - CURLU_ALLOW_SPACE | - (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); - if(uc) { - if(type != FOLLOW_FAKE) { - failf(data, "The redirect target URL could not be parsed: %s", - curl_url_strerror(uc)); - return Curl_uc_to_curlcode(uc); - } - - /* the URL could not be parsed for some reason, but since this is FAKE - mode, just duplicate the field as-is */ - newurl = strdup(newurl); - if(!newurl) - return CURLE_OUT_OF_MEMORY; - } - else { - uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - - /* Clear auth if this redirects to a different port number or protocol, - unless permitted */ - if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { - char *portnum; - int port; - bool clear = FALSE; - - if(data->set.use_port && data->state.allow_port) - /* a custom port is used */ - port = (int)data->set.use_port; - else { - uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, - CURLU_DEFAULT_PORT); - if(uc) { - free(newurl); - return Curl_uc_to_curlcode(uc); - } - port = atoi(portnum); - free(portnum); - } - if(port != data->info.conn_remote_port) { - infof(data, "Clear auth, redirects to port from %u to %u", - data->info.conn_remote_port, port); - clear = TRUE; - } - else { - char *scheme; - const struct Curl_handler *p; - uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - free(newurl); - return Curl_uc_to_curlcode(uc); - } - - p = Curl_get_scheme_handler(scheme); - if(p && (p->protocol != data->info.conn_protocol)) { - infof(data, "Clear auth, redirects scheme from %s to %s", - data->info.conn_scheme, scheme); - clear = TRUE; - } - free(scheme); - } - if(clear) { - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); - } - } - } - - if(type == FOLLOW_FAKE) { - /* we are only figuring out the new URL if we would have followed locations - but now we are done so we can get out! */ - data->info.wouldredirect = newurl; - - if(reachedmax) { - failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs); - return CURLE_TOO_MANY_REDIRECTS; - } - return CURLE_OK; - } - - if(disallowport) - data->state.allow_port = FALSE; - - if(data->state.url_alloc) - Curl_safefree(data->state.url); - - data->state.url = newurl; - data->state.url_alloc = TRUE; - Curl_req_soft_reset(&data->req, data); - infof(data, "Issue another request to this URL: '%s'", data->state.url); - - /* - * We get here when the HTTP code is 300-399 (and 401). We need to perform - * differently based on exactly what return code there was. - * - * News from 7.10.6: we can also get here on a 401 or 407, in case we act on - * an HTTP (proxy-) authentication scheme other than Basic. - */ - switch(data->info.httpcode) { - /* 401 - Act on a WWW-Authenticate, we keep on moving and do the - Authorization: XXXX header in the HTTP request code snippet */ - /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the - Proxy-Authorization: XXXX header in the HTTP request code snippet */ - /* 300 - Multiple Choices */ - /* 306 - Not used */ - /* 307 - Temporary Redirect */ - default: /* for all above (and the unknown ones) */ - /* Some codes are explicitly mentioned since I have checked RFC2616 and - * they seem to be OK to POST to. - */ - break; - case 301: /* Moved Permanently */ - /* (quote from RFC7231, section 6.4.2) - * - * Note: For historical reasons, a user agent MAY change the request - * method from POST to GET for the subsequent request. If this - * behavior is undesired, the 307 (Temporary Redirect) status code - * can be used instead. - * - * ---- - * - * Many webservers expect this, so these servers often answers to a POST - * request with an error page. To be sure that libcurl gets the page that - * most user agents would get, libcurl has to force GET. - * - * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and - * can be overridden with CURLOPT_POSTREDIR. - */ - if((data->state.httpreq == HTTPREQ_POST - || data->state.httpreq == HTTPREQ_POST_FORM - || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_301)) { - infof(data, "Switch from POST to GET"); - data->state.httpreq = HTTPREQ_GET; - Curl_creader_set_rewind(data, FALSE); - } - break; - case 302: /* Found */ - /* (quote from RFC7231, section 6.4.3) - * - * Note: For historical reasons, a user agent MAY change the request - * method from POST to GET for the subsequent request. If this - * behavior is undesired, the 307 (Temporary Redirect) status code - * can be used instead. - * - * ---- - * - * Many webservers expect this, so these servers often answers to a POST - * request with an error page. To be sure that libcurl gets the page that - * most user agents would get, libcurl has to force GET. - * - * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and - * can be overridden with CURLOPT_POSTREDIR. - */ - if((data->state.httpreq == HTTPREQ_POST - || data->state.httpreq == HTTPREQ_POST_FORM - || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_302)) { - infof(data, "Switch from POST to GET"); - data->state.httpreq = HTTPREQ_GET; - Curl_creader_set_rewind(data, FALSE); - } - break; - - case 303: /* See Other */ - /* 'See Other' location is not the resource but a substitute for the - * resource. In this case we switch the method to GET/HEAD, unless the - * method is POST and the user specified to keep it as POST. - * https://github.com/curl/curl/issues/5237#issuecomment-614641049 - */ - if(data->state.httpreq != HTTPREQ_GET && - ((data->state.httpreq != HTTPREQ_POST && - data->state.httpreq != HTTPREQ_POST_FORM && - data->state.httpreq != HTTPREQ_POST_MIME) || - !(data->set.keep_post & CURL_REDIR_POST_303))) { - data->state.httpreq = HTTPREQ_GET; - infof(data, "Switch to %s", - data->req.no_body ? "HEAD" : "GET"); - } - break; - case 304: /* Not Modified */ - /* 304 means we did a conditional request and it was "Not modified". - * We should not get any Location: header in this response! - */ - break; - case 305: /* Use Proxy */ - /* (quote from RFC2616, section 10.3.6): - * "The requested resource MUST be accessed through the proxy given - * by the Location field. The Location field gives the URI of the - * proxy. The recipient is expected to repeat this single request - * via the proxy. 305 responses MUST only be generated by origin - * servers." - */ - break; - } - Curl_pgrsTime(data, TIMER_REDIRECT); - Curl_pgrsResetTransferSizes(data); - - return CURLE_OK; -#endif /* CURL_DISABLE_HTTP */ } static CURLMcode state_performing(struct Curl_easy *data, @@ -2215,6 +1960,7 @@ static CURLMcode state_performing(struct Curl_easy *data, multi_done(data, result, TRUE); } else if(data->req.done && !Curl_cwriter_is_paused(data)) { + const struct Curl_handler *handler = data->conn->handler; /* call this even if the readwrite function returned error */ multi_posttransfer(data); @@ -2235,7 +1981,7 @@ static CURLMcode state_performing(struct Curl_easy *data, follow = FOLLOW_RETRY; (void)multi_done(data, CURLE_OK, FALSE); /* multi_done() might return CURLE_GOT_NOTHING */ - result = multi_follow(data, newurl, follow); + result = multi_follow(data, handler, newurl, follow); if(!result) { multistate(data, MSTATE_SETUP); rc = CURLM_CALL_MULTI_PERFORM; @@ -2250,7 +1996,7 @@ static CURLMcode state_performing(struct Curl_easy *data, free(newurl); newurl = data->req.location; data->req.location = NULL; - result = multi_follow(data, newurl, FOLLOW_FAKE); + result = multi_follow(data, handler, newurl, FOLLOW_FAKE); if(result) { *stream_errorp = TRUE; result = multi_done(data, result, TRUE); @@ -2302,7 +2048,7 @@ static CURLMcode state_do(struct Curl_easy *data, } } - if(data->set.connect_only == 1) { + if(data->set.connect_only && !data->set.connect_only_ws) { /* keep connection open for application to use the socket */ connkeep(data->conn, "CONNECT_ONLY"); multistate(data, MSTATE_DONE); @@ -2359,6 +2105,7 @@ static CURLMcode state_do(struct Curl_easy *data, * unexpectedly died. If possible, send the connection back to the * CONNECT phase so we can try again. */ + const struct Curl_handler *handler = data->conn->handler; char *newurl = NULL; followtype follow = FOLLOW_NONE; CURLcode drc; @@ -2378,7 +2125,7 @@ static CURLMcode state_do(struct Curl_easy *data, if(newurl) { if(!drc || (drc == CURLE_SEND_ERROR)) { follow = FOLLOW_RETRY; - drc = multi_follow(data, newurl, follow); + drc = multi_follow(data, handler, newurl, follow); if(!drc) { multistate(data, MSTATE_SETUP); rc = CURLM_CALL_MULTI_PERFORM; @@ -3021,9 +2768,11 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles) sigpipe_apply(multi->cpool.idata, &pipe_st); Curl_cpool_multi_perform(multi); - sigpipe_restore(&pipe_st); + if(multi_ischanged(m, TRUE)) + process_pending_handles(m); + /* * Simply remove all expired timers from the splay since handles are dealt * with unconditionally by this function and curl_multi_timeout() requires @@ -3124,6 +2873,7 @@ CURLMcode curl_multi_cleanup(CURLM *m) Curl_hash_destroy(&multi->proto_hash); Curl_hash_destroy(&multi->hostcache); Curl_psl_destroy(&multi->psl); + Curl_ssl_scache_destroy(multi->ssl_scache); #ifdef USE_WINSOCK WSACloseEvent(multi->wsa_event); @@ -3619,6 +3369,9 @@ out: } sigpipe_restore(&mrc.pipe_st); + if(multi_ischanged(multi, TRUE)) + process_pending_handles(multi); + if(running_handles) *running_handles = (int)multi->num_alive; @@ -3676,9 +3429,6 @@ CURLMcode curl_multi_setopt(CURLM *m, break; case CURLMOPT_MAX_TOTAL_CONNECTIONS: multi->max_total_connections = va_arg(param, long); - /* for now, let this also decide the max number of connections - * in shutdown handling */ - multi->max_shutdown_connections = va_arg(param, long); break; /* options formerly used for pipelining */ case CURLMOPT_MAX_PIPELINE_LENGTH: @@ -4126,7 +3876,7 @@ void Curl_set_in_callback(struct Curl_easy *data, bool value) bool Curl_is_in_callback(struct Curl_easy *data) { - return (data && data->multi && data->multi->in_callback); + return data && data->multi && data->multi->in_callback; } unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) diff --git a/lib/multihandle.h b/lib/multihandle.h index 9225d2d..c0a3d09 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -106,8 +106,8 @@ struct Curl_multi { curl_push_callback push_cb; void *push_userp; - /* Hostname cache */ - struct Curl_hash hostcache; + struct Curl_hash hostcache; /* Hostname cache */ + struct Curl_ssl_scache *ssl_scache; /* TLS session pool */ #ifdef USE_LIBPSL /* PSL cache. */ @@ -148,8 +148,6 @@ struct Curl_multi { long max_total_connections; /* if >0, a fixed limit of the maximum number of connections in total */ - long max_shutdown_connections; /* if >0, a fixed limit of the maximum number - of connections in shutdown handling */ /* timer callback and user data pointer for the *socket() API */ curl_multi_timer_callback timer_cb; diff --git a/lib/multiif.h b/lib/multiif.h index fd0e215..89ede92 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -47,7 +47,8 @@ void Curl_multi_connchanged(struct Curl_multi *multi); socket, connection and dns hashes */ struct Curl_multi *Curl_multi_handle(size_t hashsize, size_t chashsize, - size_t dnssize); + size_t dnssize, + size_t sesssize); /* the write bits start at bit 16 for the *getsock() bitmap */ #define GETSOCK_WRITEBITSTART 16 diff --git a/lib/netrc.c b/lib/netrc.c index d5ee3c0..7ad81ec 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -26,7 +26,9 @@ #ifndef CURL_DISABLE_NETRC #ifdef HAVE_PWD_H +#undef __NO_NET_API /* required for AmigaOS to declare getpwuid() */ #include +#define __NO_NET_API #endif #include @@ -265,7 +267,8 @@ static int parsenetrc(struct store_netrc *store, retcode = NETRC_FAILED; /* allocation failed */ goto out; } - found |= FOUND_PASSWORD; + if(!specific_login || our_login) + found |= FOUND_PASSWORD; keyword = NONE; } else if(strcasecompare("login", tok)) @@ -274,6 +277,10 @@ static int parsenetrc(struct store_netrc *store, keyword = PASSWORD; else if(strcasecompare("machine", tok)) { /* a new machine here */ + if(found & FOUND_PASSWORD) { + done = TRUE; + break; + } state = HOSTFOUND; keyword = NONE; found = 0; @@ -309,11 +316,16 @@ static int parsenetrc(struct store_netrc *store, out: Curl_dyn_free(&token); - if(!retcode && !password && our_login) { - /* success without a password, set a blank one */ - password = strdup(""); - if(!password) - retcode = 1; /* out of memory */ + if(!retcode) { + if(!password && our_login) { + /* success without a password, set a blank one */ + password = strdup(""); + if(!password) + retcode = 1; /* out of memory */ + } + else if(!login && !password) + /* a default with no credentials */ + retcode = NETRC_FILE_MISSING; } if(!retcode) { /* success */ @@ -390,7 +402,7 @@ int Curl_parsenetrc(struct store_netrc *store, const char *host, retcode = parsenetrc(store, host, loginp, passwordp, filealloc); free(filealloc); #ifdef _WIN32 - if(retcode == NETRC_FILE_MISSING) { + if((retcode == NETRC_FILE_MISSING) || (retcode == NETRC_FAILED)) { /* fallback to the old-style "_netrc" file */ filealloc = aprintf("%s%s_netrc", home, DIR_CHAR); if(!filealloc) { diff --git a/lib/nonblock.c b/lib/nonblock.c index 6dcf42a..a59cab8 100644 --- a/lib/nonblock.c +++ b/lib/nonblock.c @@ -63,6 +63,12 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ flags &= ~O_NONBLOCK; return sfcntl(sockfd, F_SETFL, flags); +#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO) + + /* Amiga */ + long flags = nonblock ? 1L : 0L; + return IoctlSocket(sockfd, FIONBIO, (char *)&flags); + #elif defined(HAVE_IOCTL_FIONBIO) /* older Unix versions */ @@ -75,12 +81,6 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ unsigned long flags = nonblock ? 1UL : 0UL; return ioctlsocket(sockfd, (long)FIONBIO, &flags); -#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO) - - /* Amiga */ - long flags = nonblock ? 1L : 0L; - return IoctlSocket(sockfd, FIONBIO, (char *)&flags); - #elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK) /* Orbis OS */ diff --git a/lib/noproxy.c b/lib/noproxy.c index dbfafc9..78cc06f 100644 --- a/lib/noproxy.c +++ b/lib/noproxy.c @@ -71,7 +71,7 @@ UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ return FALSE; return TRUE; } - return (address == check); + return address == check; } UNITTEST bool Curl_cidr6_match(const char *ipv6, diff --git a/lib/openldap.c b/lib/openldap.c index 8c4af22..22e5bdd 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -134,6 +134,7 @@ const struct Curl_handler Curl_handler_ldap = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_LDAP, /* defport */ CURLPROTO_LDAP, /* protocol */ CURLPROTO_LDAP, /* family */ @@ -163,6 +164,7 @@ const struct Curl_handler Curl_handler_ldaps = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_LDAPS, /* defport */ CURLPROTO_LDAPS, /* protocol */ CURLPROTO_LDAP, /* family */ @@ -571,7 +573,7 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); #ifdef USE_SSL - if(conn->handler->flags & PROTOPT_SSL) + if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) return oldap_ssl_connect(data, OLDAP_SSL); if(data->set.use_ssl) { diff --git a/lib/parsedate.c b/lib/parsedate.c index 4d0a221..65b231a 100644 --- a/lib/parsedate.c +++ b/lib/parsedate.c @@ -558,7 +558,7 @@ static int parsedate(const char *date, time_t *output) if(tzoff == -1) tzoff = 0; - if((tzoff > 0) && (t > TIME_T_MAX - tzoff)) { + if((tzoff > 0) && (t > (time_t)(TIME_T_MAX - tzoff))) { *output = TIME_T_MAX; return PARSEDATE_LATER; /* time_t overflow */ } @@ -586,7 +586,7 @@ time_t curl_getdate(const char *p, const time_t *now) (void)now; /* legacy argument from the past that we ignore */ if(rc == PARSEDATE_OK) { - if(parsed == -1) + if(parsed == (time_t)-1) /* avoid returning -1 for a working scenario */ parsed++; return parsed; @@ -606,7 +606,7 @@ time_t Curl_getdate_capped(const char *p) switch(rc) { case PARSEDATE_OK: - if(parsed == -1) + if(parsed == (time_t)-1) /* avoid returning -1 for a working scenario */ parsed++; return parsed; diff --git a/lib/pingpong.c b/lib/pingpong.c index bd0d3e3..bae6dd2 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -448,7 +448,7 @@ CURLcode Curl_pp_disconnect(struct pingpong *pp) bool Curl_pp_moredata(struct pingpong *pp) { - return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf) > pp->nfinal); + return !pp->sendleft && Curl_dyn_len(&pp->recvbuf) > pp->nfinal; } #endif diff --git a/lib/pop3.c b/lib/pop3.c index db6ec04..86e9eca 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -138,6 +138,7 @@ const struct Curl_handler Curl_handler_pop3 = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_POP3, /* defport */ CURLPROTO_POP3, /* protocol */ CURLPROTO_POP3, /* family */ @@ -168,6 +169,7 @@ const struct Curl_handler Curl_handler_pop3s = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_POP3S, /* defport */ CURLPROTO_POP3S, /* protocol */ CURLPROTO_POP3, /* family */ @@ -1110,6 +1112,9 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; struct pop3_conn *pop3c = &conn->proto.pop3c; + /* Issue #16166, STLS seems to stall and time out. Revert to previous + * check, but it remains to find out why this is wrong. */ + /* if(Curl_conn_is_ssl(conn, FIRSTSOCKET) && !pop3c->ssldone) { */ if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { bool ssldone = FALSE; result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); diff --git a/lib/progress.c b/lib/progress.c index d3a1b9a..82cbeb3 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -170,6 +170,8 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, case TIMER_STARTOP: /* This is set at the start of a transfer */ data->progress.t_startop = timestamp; + data->progress.t_startqueue = timestamp; + data->progress.t_postqueue = 0; break; case TIMER_STARTSINGLE: /* This is set at the start of each single transfer */ @@ -177,12 +179,9 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, data->progress.is_t_startransfer_set = FALSE; break; case TIMER_POSTQUEUE: - /* Set when the transfer starts (after potentially having been brought - back from the waiting queue). It needs to count from t_startop and not - t_startsingle since the latter is reset when a connection is brought - back from the pending queue. */ - data->progress.t_postqueue = - Curl_timediff_us(timestamp, data->progress.t_startop); + /* Queue time is accumulative from all involved redirects */ + data->progress.t_postqueue += + Curl_timediff_us(timestamp, data->progress.t_startqueue); break; case TIMER_STARTACCEPT: data->progress.t_acceptdata = timestamp; @@ -220,6 +219,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, case TIMER_REDIRECT: data->progress.t_redirect = Curl_timediff_us(timestamp, data->progress.start); + data->progress.t_startqueue = timestamp; break; } if(delta) { @@ -312,7 +312,7 @@ timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d, if(actual < minimum) { /* if it downloaded the data faster than the limit, make it wait the difference */ - return (minimum - actual); + return minimum - actual; } return 0; diff --git a/lib/request.c b/lib/request.c index 310e4ea..d5f04e9 100644 --- a/lib/request.c +++ b/lib/request.c @@ -66,7 +66,8 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req, req->headerbytecount = 0; req->allheadercount = 0; req->deductheadercount = 0; - + req->httpversion_sent = 0; + req->httpversion = 0; result = Curl_client_start(data); if(result) return result; @@ -162,9 +163,6 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) req->no_body = data->set.opt_no_body; req->authneg = FALSE; req->shutdown = FALSE; -#ifdef USE_HYPER - req->bodywritten = FALSE; -#endif } void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) @@ -261,7 +259,7 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data) return result; } -CURLcode Curl_req_set_upload_done(struct Curl_easy *data) +static CURLcode req_set_upload_done(struct Curl_easy *data) { DEBUGASSERT(!data->req.upload_done); data->req.upload_done = TRUE; @@ -339,7 +337,7 @@ static CURLcode req_flush(struct Curl_easy *data) if(!done) return CURLE_AGAIN; } - return Curl_req_set_upload_done(data); + return req_set_upload_done(data); } return CURLE_OK; } @@ -360,8 +358,6 @@ static ssize_t add_from_client(void *reader_ctx, return (ssize_t)nread; } -#ifndef USE_HYPER - static CURLcode req_send_buffer_add(struct Curl_easy *data, const char *buf, size_t blen, size_t hds_len) @@ -378,7 +374,8 @@ static CURLcode req_send_buffer_add(struct Curl_easy *data, return CURLE_OK; } -CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req) +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req, + unsigned char httpversion) { CURLcode result; const char *buf; @@ -387,6 +384,7 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req) if(!data || !data->conn) return CURLE_FAILED_INIT; + data->req.httpversion_sent = httpversion; buf = Curl_dyn_ptr(req); blen = Curl_dyn_len(req); if(!Curl_creader_total_length(data)) { @@ -411,7 +409,6 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req) } return CURLE_OK; } -#endif /* !USE_HYPER */ bool Curl_req_sendbuf_empty(struct Curl_easy *data) { @@ -464,7 +461,7 @@ CURLcode Curl_req_abort_sending(struct Curl_easy *data) data->req.upload_aborted = TRUE; /* no longer KEEP_SEND and KEEP_SEND_PAUSE */ data->req.keepon &= ~KEEP_SENDBITS; - return Curl_req_set_upload_done(data); + return req_set_upload_done(data); } return CURLE_OK; } diff --git a/lib/request.h b/lib/request.h index bb72247..4c77be9 100644 --- a/lib/request.h +++ b/lib/request.h @@ -81,10 +81,11 @@ struct SingleRequest { first one */ curl_off_t offset; /* possible resume offset read from the Content-Range: header */ - int httpversion; /* Version in response (09, 10, 11, etc.) */ int httpcode; /* error code from the 'HTTP/1.? XXX' or 'RTSP/1.? XXX' line */ int keepon; + unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */ + unsigned char httpversion; /* Version in response (09, 10, 11, etc.) */ enum upgrade101 upgr101; /* 101 upgrade state */ /* Client Writer stack, handles transfer- and content-encodings, protocol @@ -152,9 +153,6 @@ struct SingleRequest { BIT(sendbuf_init); /* sendbuf is initialized */ BIT(shutdown); /* request end will shutdown connection */ BIT(shutdown_err_ignore); /* errors in shutdown will not fail request */ -#ifdef USE_HYPER - BIT(bodywritten); -#endif }; /** @@ -196,18 +194,17 @@ void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data); */ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data); -#ifndef USE_HYPER /** * Send request headers. If not all could be sent * they will be buffered. Use `Curl_req_flush()` to make sure * bytes are really send. * @param data the transfer making the request * @param buf the complete header bytes, no body + * @param httpversion version used in request (09, 10, 11, etc.) * @return CURLE_OK (on blocking with *pnwritten == 0) or error. */ -CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf); - -#endif /* !USE_HYPER */ +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf, + unsigned char httpversion); /** * TRUE iff the request has sent all request headers and data. diff --git a/lib/rtsp.c b/lib/rtsp.c index ecefd13..4325240 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_RTSP) && !defined(USE_HYPER) +#if !defined(CURL_DISABLE_RTSP) #include "urldata.h" #include @@ -117,6 +117,7 @@ const struct Curl_handler Curl_handler_rtsp = { ZERO_NULL, /* write_resp_hd */ rtsp_conncheck, /* connection_check */ ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ PORT_RTSP, /* defport */ CURLPROTO_RTSP, /* protocol */ CURLPROTO_RTSP, /* family */ @@ -230,6 +231,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) Curl_RtspReq rtspreq = data->set.rtspreq; struct RTSP *rtsp = data->req.p.rtsp; struct dynbuf req_buffer; + unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort of... */ const char *p_request = NULL; const char *p_session_id = NULL; @@ -499,7 +501,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) goto out; } - result = Curl_add_custom_headers(data, FALSE, &req_buffer); + result = Curl_add_custom_headers(data, FALSE, httpversion, &req_buffer); if(result) goto out; @@ -585,7 +587,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE); /* issue the request */ - result = Curl_req_send(data, &req_buffer); + result = Curl_req_send(data, &req_buffer, httpversion); if(result) { failf(data, "Failed sending RTSP request"); goto out; @@ -1043,4 +1045,4 @@ CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport) } -#endif /* CURL_DISABLE_RTSP or using Hyper */ +#endif /* CURL_DISABLE_RTSP */ diff --git a/lib/rtsp.h b/lib/rtsp.h index 41b0950..68f6f4f 100644 --- a/lib/rtsp.h +++ b/lib/rtsp.h @@ -23,9 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifdef USE_HYPER -#define CURL_DISABLE_RTSP 1 -#endif #ifndef CURL_DISABLE_RTSP diff --git a/lib/select.c b/lib/select.c index c1779ad..ef8d554 100644 --- a/lib/select.c +++ b/lib/select.c @@ -78,7 +78,7 @@ int Curl_wait_ms(timediff_t timeout_ms) return -1; } #if defined(MSDOS) - delay(timeout_ms); + delay((unsigned int)timeout_ms); #elif defined(_WIN32) /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ #if TIMEDIFF_T_MAX >= ULONG_MAX @@ -488,43 +488,47 @@ CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds, return CURLE_OK; } -void Curl_waitfds_init(struct curl_waitfds *cwfds, +void Curl_waitfds_init(struct Curl_waitfds *cwfds, struct curl_waitfd *static_wfds, unsigned int static_count) { DEBUGASSERT(cwfds); - DEBUGASSERT(static_wfds); + DEBUGASSERT(static_wfds || !static_count); memset(cwfds, 0, sizeof(*cwfds)); cwfds->wfds = static_wfds; cwfds->count = static_count; } -static CURLcode cwfds_add_sock(struct curl_waitfds *cwfds, - curl_socket_t sock, short events) +static unsigned int cwfds_add_sock(struct Curl_waitfds *cwfds, + curl_socket_t sock, short events) { int i; - + if(!cwfds->wfds) { + DEBUGASSERT(!cwfds->count && !cwfds->n); + return 1; + } if(cwfds->n <= INT_MAX) { for(i = (int)cwfds->n - 1; i >= 0; --i) { if(sock == cwfds->wfds[i].fd) { cwfds->wfds[i].events |= events; - return CURLE_OK; + return 0; } } } /* not folded, add new entry */ - if(cwfds->n >= cwfds->count) - return CURLE_OUT_OF_MEMORY; - cwfds->wfds[cwfds->n].fd = sock; - cwfds->wfds[cwfds->n].events = events; - ++cwfds->n; - return CURLE_OK; + if(cwfds->n < cwfds->count) { + cwfds->wfds[cwfds->n].fd = sock; + cwfds->wfds[cwfds->n].events = events; + ++cwfds->n; + } + return 1; } -CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds, - struct easy_pollset *ps) +unsigned int Curl_waitfds_add_ps(struct Curl_waitfds *cwfds, + struct easy_pollset *ps) { size_t i; + unsigned int need = 0; DEBUGASSERT(cwfds); DEBUGASSERT(ps); @@ -534,10 +538,8 @@ CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds, events |= CURL_WAIT_POLLIN; if(ps->actions[i] & CURL_POLL_OUT) events |= CURL_WAIT_POLLOUT; - if(events) { - if(cwfds_add_sock(cwfds, ps->sockets[i], events)) - return CURLE_OUT_OF_MEMORY; - } + if(events) + need += cwfds_add_sock(cwfds, ps->sockets[i], events); } - return CURLE_OK; + return need; } diff --git a/lib/select.h b/lib/select.h index f01acbd..608395f 100644 --- a/lib/select.h +++ b/lib/select.h @@ -130,18 +130,17 @@ CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds, CURLcode Curl_pollfds_add_sock(struct curl_pollfds *cpfds, curl_socket_t sock, short events); -struct curl_waitfds { +struct Curl_waitfds { struct curl_waitfd *wfds; unsigned int n; unsigned int count; }; -void Curl_waitfds_init(struct curl_waitfds *cwfds, +void Curl_waitfds_init(struct Curl_waitfds *cwfds, struct curl_waitfd *static_wfds, unsigned int static_count); -CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds, - struct easy_pollset *ps); - +unsigned int Curl_waitfds_add_ps(struct Curl_waitfds *cwfds, + struct easy_pollset *ps); #endif /* HEADER_CURL_SELECT_H */ diff --git a/lib/sendf.c b/lib/sendf.c index 30a3517..bffbd64 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -316,9 +316,6 @@ static CURLcode cw_download_write(struct Curl_easy *data, } /* Update stats, write and report progress */ data->req.bytecount += nwrite; -#ifdef USE_HYPER - data->req.bodywritten = TRUE; -#endif result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); if(result) return result; @@ -497,22 +494,6 @@ struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, return NULL; } -void Curl_cwriter_remove_by_name(struct Curl_easy *data, - const char *name) -{ - struct Curl_cwriter **anchor = &data->req.writer_stack; - - while(*anchor) { - if(!strcmp(name, (*anchor)->cwt->name)) { - struct Curl_cwriter *w = (*anchor); - *anchor = w->next; - Curl_cwriter_free(data, w); - continue; - } - anchor = &((*anchor)->next); - } -} - bool Curl_cwriter_is_paused(struct Curl_easy *data) { return Curl_cw_out_is_paused(data); diff --git a/lib/sendf.h b/lib/sendf.h index dc1b82e..41ca865 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -166,9 +166,6 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data, struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, const struct Curl_cwtype *cwt); -void Curl_cwriter_remove_by_name(struct Curl_easy *data, - const char *name); - struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, const char *name); diff --git a/lib/setopt.c b/lib/setopt.c index 0bae6ba..e264c73 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -644,13 +644,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, break; case CURLOPT_HTTP09_ALLOWED: -#ifdef USE_HYPER - /* Hyper does not support HTTP/0.9 */ - if(enabled) - return CURLE_BAD_FUNCTION_ARGUMENT; -#else data->set.http09_allowed = enabled; -#endif break; #endif /* ! CURL_DISABLE_HTTP */ @@ -923,6 +917,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, break; #endif +#ifdef HAVE_GSSAPI case CURLOPT_GSSAPI_DELEGATION: /* * GSS-API credential delegation bitmask @@ -930,6 +925,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, data->set.gssapi_delegation = (unsigned char)uarg& (CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG); break; +#endif case CURLOPT_SSL_VERIFYPEER: /* * Enable peer SSL verifying. @@ -991,7 +987,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, /* * Enable TLS false start. */ - if(!Curl_ssl_false_start(data)) + if(!Curl_ssl_false_start()) return CURLE_NOT_BUILT_IN; data->set.ssl.falsestart = enabled; @@ -1110,7 +1106,8 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, */ if(arg > 2) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.connect_only = (unsigned char)arg; + data->set.connect_only = !!arg; + data->set.connect_only_ws = (arg == 2); break; case CURLOPT_SSL_SESSIONID_CACHE: @@ -1588,8 +1585,8 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, data->hsts = NULL; #endif #ifdef USE_SSL - if(data->share->sslsession == data->state.session) - data->state.session = NULL; + if(data->share->ssl_scache == data->state.ssl_scache) + data->state.ssl_scache = data->multi ? data->multi->ssl_scache : NULL; #endif #ifdef USE_LIBPSL if(data->psl == &data->share->psl) @@ -1632,10 +1629,8 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, } #endif #ifdef USE_SSL - if(data->share->sslsession) { - data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; - data->state.session = data->share->sslsession; - } + if(data->share->ssl_scache) + data->state.ssl_scache = data->share->ssl_scache; #endif #ifdef USE_LIBPSL if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) @@ -2111,7 +2106,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * The URL to fetch. */ if(data->state.url_alloc) { - /* the already set URL is allocated, free it first! */ Curl_safefree(data->state.url); data->state.url_alloc = FALSE; } @@ -2199,6 +2193,13 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, /* * pass CURLU to set URL */ + if(data->state.url_alloc) { + Curl_safefree(data->state.url); + data->state.url_alloc = FALSE; + } + else + data->state.url = NULL; + Curl_safefree(data->set.str[STRING_SET_URL]); data->set.uh = (CURLU *)ptr; break; case CURLOPT_SSLCERT: diff --git a/lib/setup-vms.h b/lib/setup-vms.h index 33b74db..59b69b6 100644 --- a/lib/setup-vms.h +++ b/lib/setup-vms.h @@ -370,7 +370,7 @@ static struct passwd *vms_getpwuid(uid_t uid) #define USE_UPPERCASE_KRBAPI 1 -/* AI_NUMERICHOST needed for IP V6 support in Curl */ +/* AI_NUMERICHOST needed for IP V6 support in curl */ #ifdef HAVE_NETDB_H #include #ifndef AI_NUMERICHOST diff --git a/lib/setup-win32.h b/lib/setup-win32.h index a297bdc..70d83ad 100644 --- a/lib/setup-win32.h +++ b/lib/setup-win32.h @@ -77,17 +77,6 @@ # if defined(_UNICODE) && !defined(UNICODE) # error "_UNICODE is defined but UNICODE is not defined" # endif -/* - * Do not include unneeded stuff in Windows headers to avoid compiler - * warnings and macro clashes. - * Make sure to define this macro before including any Windows headers. - */ -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# ifndef NOGDI -# define NOGDI -# endif # include # include # include diff --git a/lib/sha256.c b/lib/sha256.c index c5bb921..4af858e 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -26,7 +26,7 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ - || defined(USE_LIBSSH2) + || defined(USE_LIBSSH2) || defined(USE_SSL) #include "warnless.h" #include "curl_sha256.h" diff --git a/lib/share.c b/lib/share.c index 6cdba18..4145e0c 100644 --- a/lib/share.c +++ b/lib/share.c @@ -30,6 +30,7 @@ #include "share.h" #include "psl.h" #include "vtls/vtls.h" +#include "vtls/vtls_scache.h" #include "hsts.h" #include "url.h" @@ -108,12 +109,13 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL - if(!share->sslsession) { - share->max_ssl_sessions = 8; - share->sslsession = calloc(share->max_ssl_sessions, - sizeof(struct Curl_ssl_session)); - share->sessionage = 0; - if(!share->sslsession) + if(!share->ssl_scache) { + /* There is no way (yet) for the application to configure the + * session cache size, shared between many transfers. As for curl + * itself, a high session count will impact startup time. Also, the + * scache is not optimized for several hundreds of peers. So, + * keep it at a reasonable level. */ + if(Curl_ssl_scache_create(25, 2, &share->ssl_scache)) res = CURLSHE_NOMEM; } #else @@ -174,7 +176,10 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL - Curl_safefree(share->sslsession); + if(share->ssl_scache) { + Curl_ssl_scache_destroy(share->ssl_scache); + share->ssl_scache = NULL; + } #else res = CURLSHE_NOT_BUILT_IN; #endif @@ -245,11 +250,9 @@ curl_share_cleanup(CURLSH *sh) #endif #ifdef USE_SSL - if(share->sslsession) { - size_t i; - for(i = 0; i < share->max_ssl_sessions; i++) - Curl_ssl_kill_session(&(share->sslsession[i])); - free(share->sslsession); + if(share->ssl_scache) { + Curl_ssl_scache_destroy(share->ssl_scache); + share->ssl_scache = NULL; } #endif diff --git a/lib/share.h b/lib/share.h index 124f704..d0cdb1b 100644 --- a/lib/share.h +++ b/lib/share.h @@ -31,6 +31,8 @@ #include "urldata.h" #include "conncache.h" +struct Curl_ssl_scache; + #define CURL_GOOD_SHARE 0x7e117a1e #define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE) @@ -58,9 +60,7 @@ struct Curl_share { struct hsts *hsts; #endif #ifdef USE_SSL - struct Curl_ssl_session *sslsession; - size_t max_ssl_sessions; - long sessionage; + struct Curl_ssl_scache *ssl_scache; #endif }; @@ -68,4 +68,9 @@ CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data, curl_lock_access); CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data); +/* convenience macro to check if this handle is using a shared SSL spool */ +#define CURL_SHARE_ssl_scache(data) (data->share && \ + (data->share->specifier & \ + (1<state == SMB_CONNECTING) { #ifdef USE_SSL - if((conn->handler->flags & PROTOPT_SSL)) { + if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { bool ssl_done = FALSE; result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssl_done); if(result && result != CURLE_AGAIN) @@ -915,7 +917,7 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) */ static void get_posix_time(time_t *out, curl_off_t timestamp) { - timestamp -= 116444736000000000; + timestamp -= CURL_OFF_T_C(116444736000000000); timestamp /= 10000000; #if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T if(timestamp > TIME_T_MAX) diff --git a/lib/smtp.c b/lib/smtp.c index d854d36..7c631a1 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -135,6 +135,7 @@ const struct Curl_handler Curl_handler_smtp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_SMTP, /* defport */ CURLPROTO_SMTP, /* protocol */ CURLPROTO_SMTP, /* family */ @@ -165,6 +166,7 @@ const struct Curl_handler Curl_handler_smtps = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_SMTPS, /* defport */ CURLPROTO_SMTPS, /* protocol */ CURLPROTO_SMTP, /* family */ @@ -1286,7 +1288,7 @@ static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; struct smtp_conn *smtpc = &conn->proto.smtpc; - if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { + if(Curl_conn_is_ssl(conn, FIRSTSOCKET) && !smtpc->ssldone) { bool ssldone = FALSE; result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); smtpc->ssldone = ssldone; diff --git a/lib/socketpair.c b/lib/socketpair.c index b14f5a5..c4f558e 100644 --- a/lib/socketpair.c +++ b/lib/socketpair.c @@ -53,7 +53,7 @@ int Curl_pipe(curl_socket_t socks[2], bool nonblocking) return -1; #ifdef HAVE_FCNTL if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || - fcntl(socks[1], F_SETFD, FD_CLOEXEC) ) { + fcntl(socks[1], F_SETFD, FD_CLOEXEC)) { close(socks[0]); close(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index f6fd55d..776dbda 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -42,6 +42,11 @@ #include "curl_memory.h" #include "memdebug.h" +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + #define MAX_GSS_LEN 1024 static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; @@ -537,4 +542,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_OK; } +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + #endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */ diff --git a/lib/strcase.c b/lib/strcase.c index b22dd31..112aedb 100644 --- a/lib/strcase.c +++ b/lib/strcase.c @@ -113,7 +113,7 @@ int curl_strequal(const char *first, const char *second) return casecompare(first, second); /* if both pointers are NULL then treat them as equal */ - return (NULL == first && NULL == second); + return NULL == first && NULL == second; } static int ncasecompare(const char *first, const char *second, size_t max) @@ -139,7 +139,7 @@ int curl_strnequal(const char *first, const char *second, size_t max) return ncasecompare(first, second, max); /* if both pointers are NULL then treat them as equal if max is non-zero */ - return (NULL == first && NULL == second && max); + return NULL == first && NULL == second && max; } /* Copy an upper case version of the string from src to dest. The * strings may overlap. No more than n characters of the string are copied diff --git a/lib/strerror.c b/lib/strerror.c index 6b67a90..20495c9 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -808,7 +808,7 @@ get_winapi_error(int err, char *buf, size_t buflen) *p = '\0'; } - return (*buf ? buf : NULL); + return *buf ? buf : NULL; } #endif /* _WIN32 || _WIN32_WCE */ @@ -891,7 +891,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) } #else { - /* !checksrc! disable STRERROR 1 */ + /* !checksrc! disable BANNEDFUNC 1 */ const char *msg = strerror(err); if(msg) msnprintf(buf, buflen, "%s", msg); diff --git a/lib/strparse.c b/lib/strparse.c new file mode 100644 index 0000000..dce0825 --- /dev/null +++ b/lib/strparse.c @@ -0,0 +1,136 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "strparse.h" + +/* Get a word until the first DELIM or end of string. At least one byte long. + return non-zero on error */ +int Curl_str_until(char **linep, struct Curl_str *out, + const size_t max, char delim) +{ + char *s = *linep; + size_t len = 0; + DEBUGASSERT(linep && *linep && out && max && delim); + + out->str = NULL; + out->len = 0; + while(*s && (*s != delim)) { + s++; + if(++len > max) { + return STRE_BIG; + } + } + if(!len) + return STRE_SHORT; + out->str = *linep; + out->len = len; + *linep = s; /* point to the first byte after the word */ + return STRE_OK; +} + +/* Get a word until the first space or end of string. At least one byte long. + return non-zero on error */ +int Curl_str_word(char **linep, struct Curl_str *out, + const size_t max) +{ + return Curl_str_until(linep, out, max, ' '); +} + + +/* Get a "quoted" word. No escaping possible. + return non-zero on error */ +int Curl_str_quotedword(char **linep, struct Curl_str *out, + const size_t max) +{ + char *s = *linep; + size_t len = 0; + DEBUGASSERT(linep && *linep && out && max); + + out->str = NULL; + out->len = 0; + if(*s != '\"') + return STRE_BEGQUOTE; + s++; + while(*s && (*s != '\"')) { + s++; + if(++len > max) + return STRE_BIG; + } + if(*s != '\"') + return STRE_ENDQUOTE; + out->str = (*linep) + 1; + out->len = len; + *linep = s + 1; + return STRE_OK; +} + +/* Advance over a single character. + return non-zero on error */ +int Curl_str_single(char **linep, char byte) +{ + DEBUGASSERT(linep && *linep); + if(**linep != byte) + return STRE_BYTE; + (*linep)++; /* move over it */ + return STRE_OK; +} + +/* Advance over a single space. + return non-zero on error */ +int Curl_str_singlespace(char **linep) +{ + return Curl_str_single(linep, ' '); +} + +/* Get an unsigned number. Leading zeroes are accepted. + return non-zero on error */ +int Curl_str_number(char **linep, size_t *nump, size_t max) +{ + size_t num = 0; + DEBUGASSERT(linep && *linep && nump); + *nump = 0; + while(ISDIGIT(**linep)) { + int n = **linep - '0'; + if(num > ((SIZE_T_MAX - n) / 10)) + return STRE_OVERFLOW; + num = num * 10 + n; + if(num > max) + return STRE_BIG; /** too big */ + (*linep)++; + } + *nump = num; + return STRE_OK; +} + +/* CR or LF + return non-zero on error */ +int Curl_str_newline(char **linep) +{ + DEBUGASSERT(linep && *linep); + if(ISNEWLINE(**linep)) { + (*linep)++; + return STRE_OK; /* yessir */ + } + return STRE_NEWLINE; +} diff --git a/lib/strparse.h b/lib/strparse.h new file mode 100644 index 0000000..189927b --- /dev/null +++ b/lib/strparse.h @@ -0,0 +1,71 @@ +#ifndef HEADER_CURL_STRPARSE_H +#define HEADER_CURL_STRPARSE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#define STRE_OK 0 +#define STRE_BIG 1 +#define STRE_SHORT 2 +#define STRE_BEGQUOTE 3 +#define STRE_ENDQUOTE 4 +#define STRE_BYTE 5 +#define STRE_NEWLINE 6 +#define STRE_OVERFLOW 7 + +struct Curl_str { + char *str; + size_t len; +}; + +/* Get a word until the first space + return non-zero on error */ +int Curl_str_word(char **linep, struct Curl_str *out, const size_t max); + +/* Get a word until the first DELIM or end of string + return non-zero on error */ +int Curl_str_until(char **linep, struct Curl_str *out, const size_t max, + char delim); + +/* Get a "quoted" word. No escaping possible. + return non-zero on error */ +int Curl_str_quotedword(char **linep, struct Curl_str *out, const size_t max); + +/* Advance over a single character. + return non-zero on error */ +int Curl_str_single(char **linep, char byte); + +/* Advance over a single space. + return non-zero on error */ +int Curl_str_singlespace(char **linep); + +/* Get an unsigned number + return non-zero on error */ +int Curl_str_number(char **linep, size_t *nump, size_t max); + +/* Check for CR or LF + return non-zero on error */ +int Curl_str_newline(char **linep); + +#endif /* HEADER_CURL_STRPARSE_H */ diff --git a/lib/strtoofft.c b/lib/strtoofft.c index f1c7ba2..05536c1 100644 --- a/lib/strtoofft.c +++ b/lib/strtoofft.c @@ -39,7 +39,7 @@ # ifdef HAVE_STRTOLL # define strtooff strtoll # else -# if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64) +# if defined(_MSC_VER) && (_MSC_VER >= 1300) # if defined(_SAL_VERSION) _Check_return_ _CRTIMP __int64 __cdecl _strtoi64( _In_z_ const char *_String, diff --git a/lib/telnet.c b/lib/telnet.c index 64d552d..589f9da 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -190,6 +190,7 @@ const struct Curl_handler Curl_handler_telnet = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_TELNET, /* defport */ CURLPROTO_TELNET, /* protocol */ CURLPROTO_TELNET, /* family */ @@ -695,7 +696,10 @@ static void printsub(struct Curl_easy *data, infof(data, ", not IAC SE) "); } } - length -= 2; + if(length >= 2) + length -= 2; + else /* bad input */ + return; } if(length < 1) { infof(data, "(Empty suboption?)"); @@ -775,8 +779,7 @@ static void printsub(struct Curl_easy *data, #ifdef _MSC_VER #pragma warning(push) -/* warning C4706: assignment within conditional expression */ -#pragma warning(disable:4706) +#pragma warning(disable:4706) /* assignment within conditional expression */ #endif static bool str_is_nonascii(const char *str) { diff --git a/lib/tftp.c b/lib/tftp.c index e92e512..3f214d5 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -185,6 +185,7 @@ const struct Curl_handler Curl_handler_tftp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_TFTP, /* defport */ CURLPROTO_TFTP, /* protocol */ CURLPROTO_TFTP, /* family */ @@ -524,7 +525,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, not have a size_t argument, like older unixes that want an 'int' */ senddata = sendto(state->sockfd, (void *)state->spacket.data, (SEND_TYPE_ARG3)sbytes, 0, - &data->conn->remote_addr->curl_sa_addr, + (struct sockaddr *)&data->conn->remote_addr->curl_sa_addr, (curl_socklen_t)data->conn->remote_addr->addrlen); if(senddata != (ssize_t)sbytes) { char buffer[STRERROR_LEN]; diff --git a/lib/transfer.c b/lib/transfer.c index d7d3d16..c4b23a8 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -176,9 +176,9 @@ static bool xfer_recv_shutdown_started(struct Curl_easy *data) int sockindex; if(!data || !data->conn) - return CURLE_FAILED_INIT; + return FALSE; if(data->conn->sockfd == CURL_SOCKET_BAD) - return CURLE_FAILED_INIT; + return FALSE; sockindex = (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]); return Curl_shutdown_started(data, sockindex); } @@ -434,15 +434,6 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp) data->state.select_bits = 0; } -#ifdef USE_HYPER - if(data->conn->datastream) { - result = data->conn->datastream(data, data->conn, &didwhat, - CURL_CSELECT_OUT|CURL_CSELECT_IN); - if(result || data->req.done) - goto out; - } - else { -#endif /* We go ahead and do a read if we have a readable socket or if the stream was rewound (in which case we have data in a buffer) */ if(k->keepon & KEEP_RECV) { @@ -457,9 +448,6 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp) if(result) goto out; } -#ifdef USE_HYPER - } -#endif if(!didwhat) { /* Transfer wanted to send/recv, but nothing was possible. */ @@ -538,22 +526,17 @@ void Curl_init_CONNECT(struct Curl_easy *data) */ CURLcode Curl_pretransfer(struct Curl_easy *data) { - CURLcode result; + CURLcode result = CURLE_OK; - if(!data->state.url && !data->set.uh) { + if(!data->set.str[STRING_SET_URL] && !data->set.uh) { /* we cannot do anything without URL */ failf(data, "No URL set"); return CURLE_URL_MALFORMAT; } - /* since the URL may have been redirected in a previous use of this handle */ - if(data->state.url_alloc) { - /* the already set URL is allocated, free it first! */ - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } - - if(!data->state.url && data->set.uh) { + /* CURLOPT_CURLU overrides CURLOPT_URL and the contents of the CURLU handle + is allowed to be changed by the user between transfers */ + if(data->set.uh) { CURLUcode uc; free(data->set.str[STRING_SET_URL]); uc = curl_url_get(data->set.uh, @@ -564,6 +547,14 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } } + /* since the URL may have been redirected in a previous use of this handle */ + if(data->state.url_alloc) { + Curl_safefree(data->state.url); + data->state.url_alloc = FALSE; + } + + data->state.url = data->set.str[STRING_SET_URL]; + if(data->set.postfields && data->set.set_resume_from) { /* we cannot */ failf(data, "cannot mix POSTFIELDS with RESUME_FROM"); @@ -575,14 +566,12 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.list_only = data->set.list_only; #endif data->state.httpreq = data->set.method; - data->state.url = data->set.str[STRING_SET_URL]; - /* Init the SSL session ID cache here. We do it here since we want to do it - after the *_setopt() calls (that could specify the size of the cache) but - before any transfer takes place. */ - result = Curl_ssl_initsessions(data, data->set.general_ssl.max_ssl_sessions); - if(result) - return result; +#ifdef USE_SSL + if(!data->state.ssl_scache) + /* There was no ssl session cache set via a share, use the multi one */ + data->state.ssl_scache = data->multi->ssl_scache; +#endif data->state.requests = 0; data->state.followlocation = 0; /* reset the location-follow counter */ @@ -793,7 +782,7 @@ static void xfer_setup( DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1)); DEBUGASSERT(!shutdown || (sockindex == -1) || (writesockindex == -1)); - if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) { + if(Curl_conn_is_multiplex(conn, FIRSTSOCKET) || want_send) { /* when multiplexing, the read/write sockets need to be the same! */ conn->sockfd = sockindex == -1 ? ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) : @@ -984,9 +973,9 @@ bool Curl_xfer_is_blocked(struct Curl_easy *data) bool want_send = ((data)->req.keepon & KEEP_SEND); bool want_recv = ((data)->req.keepon & KEEP_RECV); if(!want_send) - return (want_recv && Curl_cwriter_is_paused(data)); + return want_recv && Curl_cwriter_is_paused(data); else if(!want_recv) - return (want_send && Curl_creader_is_paused(data)); + return want_send && Curl_creader_is_paused(data); else return Curl_creader_is_paused(data) && Curl_cwriter_is_paused(data); } diff --git a/lib/transfer.h b/lib/transfer.h index 8c9b88c..b67f8a8 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -33,15 +33,6 @@ void Curl_init_CONNECT(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); -typedef enum { - FOLLOW_NONE, /* not used within the function, just a placeholder to - allow initing to this */ - FOLLOW_FAKE, /* only records stuff, not actually following */ - FOLLOW_RETRY, /* set if this is a request retry as opposed to a real - redirect following */ - FOLLOW_REDIR /* a full true redirect */ -} followtype; - CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp); int Curl_single_getsock(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *socks); diff --git a/lib/url.c b/lib/url.c index 436edd8..0f3d100 100644 --- a/lib/url.c +++ b/lib/url.c @@ -382,8 +382,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) #endif set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ - /* Set the default size of the SSL session ID cache */ - set->general_ssl.max_ssl_sessions = 5; /* Timeout every 24 hours by default */ set->general_ssl.ca_cache_timeout = 24 * 60 * 60; @@ -839,8 +837,8 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data, static bool ssh_config_matches(struct connectdata *one, struct connectdata *two) { - return (Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) && - Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub)); + return Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) && + Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub); } #else #define ssh_config_matches(x,y) FALSE @@ -958,12 +956,12 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) 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 */ + if((!(needle->handler->flags&PROTOPT_SSL) != + !Curl_conn_is_ssl(conn, FIRSTSOCKET)) && + !(get_protocol_family(conn->handler) == needle->handler->protocol && + conn->bits.tls_upgraded)) + /* Deny `conn` if it is not fit for `needle`'s SSL needs, + * UNLESS `conn` is the same protocol family and was upgraded to SSL. */ return FALSE; #ifndef CURL_DISABLE_PROXY @@ -1004,7 +1002,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(match->may_multiplex && (data->state.httpwant == CURL_HTTP_VERSION_2_0) && (needle->handler->protocol & CURLPROTO_HTTP) && - !conn->httpversion) { + !conn->httpversion_seen) { if(data->set.pipewait) { infof(data, "Server upgrade does not support multiplex yet, wait"); match->found = NULL; @@ -1027,10 +1025,12 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) } } +#ifdef HAVE_GSSAPI /* 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; +#endif /* If looking for HTTP and the HTTP version we want is less * than the HTTP version of conn, continue looking. @@ -1038,17 +1038,18 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) * so we take any existing connection. */ if((needle->handler->protocol & PROTO_FAMILY_HTTP) && (data->state.httpwant != CURL_HTTP_VERSION_2TLS)) { - if((conn->httpversion >= 20) && + unsigned char httpversion = Curl_conn_http_version(data); + if((httpversion >= 20) && (data->state.httpwant < CURL_HTTP_VERSION_2_0)) { DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T " with httpversion=%d, we want a version less than h2", - conn->connection_id, conn->httpversion)); + conn->connection_id, httpversion)); } - if((conn->httpversion >= 30) && + if((httpversion >= 30) && (data->state.httpwant < CURL_HTTP_VERSION_3)) { DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T " with httpversion=%d, we want a version less than h3", - conn->connection_id, conn->httpversion)); + conn->connection_id, httpversion)); return FALSE; } } @@ -1390,8 +1391,9 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; conn->lastused = conn->created; +#ifdef HAVE_GSSAPI conn->gssapi_delegation = data->set.gssapi_delegation; - +#endif return conn; error: @@ -1827,7 +1829,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, /* HSTS upgrade */ if(data->hsts && strcasecompare("http", data->state.up.scheme)) { /* This MUST use the IDN decoded name */ - if(Curl_hsts(data->hsts, conn->host.name, TRUE)) { + if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { char *url; Curl_safefree(data->state.up.scheme); uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); @@ -3137,14 +3139,14 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, /* protocol version switch */ switch(as->dst.alpnid) { case ALPN_h1: - conn->httpversion = 11; + data->state.httpwant = CURL_HTTP_VERSION_1_1; break; case ALPN_h2: - conn->httpversion = 20; + data->state.httpwant = CURL_HTTP_VERSION_2_0; break; case ALPN_h3: conn->transport = TRNSPRT_QUIC; - conn->httpversion = 30; + data->state.httpwant = CURL_HTTP_VERSION_3; break; default: /* should not be possible */ break; diff --git a/lib/urlapi.c b/lib/urlapi.c index 98c8f6f..d9b04e3 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -86,6 +86,9 @@ struct Curl_URL { #define DEFAULT_SCHEME "https" +static CURLUcode parseurl_and_replace(const char *url, CURLU *u, + unsigned int flags); + static void free_urlhandle(struct Curl_URL *u) { free(u->scheme); @@ -244,92 +247,47 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, /* * Concatenate a relative URL to a base URL making it absolute. - * URL-encodes any spaces. - * The returned pointer must be freed by the caller unless NULL - * (returns NULL on out of memory). * * Note that this function destroys the 'base' string. */ -static CURLcode concat_url(char *base, const char *relurl, char **newurl) +static CURLUcode redirect_url(char *base, const char *relurl, + CURLU *u, unsigned int flags) { - /*** - TRY to append this new path to the old URL - to the right of the host part. Oh crap, this is doomed to cause - problems in the future... - */ - struct dynbuf newest; - char *protsep; - char *pathsep; + struct dynbuf urlbuf; bool host_changed = FALSE; const char *useurl = relurl; CURLcode result = CURLE_OK; CURLUcode uc; - bool skip_slash = FALSE; - *newurl = NULL; - /* protsep points to the start of the hostname */ - protsep = strstr(base, "//"); + char *protsep = strstr(base, "//"); + DEBUGASSERT(protsep); if(!protsep) protsep = base; else protsep += 2; /* pass the slashes */ - if('/' != relurl[0]) { - int level = 0; - - /* First we need to find out if there is a ?-letter in the URL, + if(('/' != relurl[0]) && ('#' != relurl[0])) { + /* First we need to find out if there is a ?-letter in the original URL, and cut it and the right-side of that off */ - pathsep = strchr(protsep, '?'); + char *pathsep = strchr(protsep, '?'); if(pathsep) *pathsep = 0; - - /* we have a relative path to append to the last slash if there is one - available, or the new URL is just a query string (starts with a '?') or - a fragment (starts with '#') we append the new one at the end of the - current URL */ - if((useurl[0] != '?') && (useurl[0] != '#')) { - pathsep = strrchr(protsep, '/'); + else { + /* if not, cut off the potential fragment */ + pathsep = strchr(protsep, '#'); if(pathsep) *pathsep = 0; + } - /* Check if there is any slash after the hostname, and if so, remember - that position instead */ - pathsep = strchr(protsep, '/'); + /* if the redirect-to piece is not just a query, cut the path after the + last slash */ + if(useurl[0] != '?') { + pathsep = strrchr(protsep, '/'); if(pathsep) - protsep = pathsep + 1; - else - protsep = NULL; - - /* now deal with one "./" or any amount of "../" in the newurl - and act accordingly */ - - if((useurl[0] == '.') && (useurl[1] == '/')) - useurl += 2; /* just skip the "./" */ - - while((useurl[0] == '.') && - (useurl[1] == '.') && - (useurl[2] == '/')) { - level++; - useurl += 3; /* pass the "../" */ - } - - if(protsep) { - while(level--) { - /* cut off one more level from the right of the original URL */ - pathsep = strrchr(protsep, '/'); - if(pathsep) - *pathsep = 0; - else { - *protsep = 0; - break; - } - } - } + pathsep[1] = 0; /* leave the slash */ } - else - skip_slash = TRUE; } - else { + else if('/' == relurl[0]) { /* We got a new absolute path for this server */ if(relurl[1] == '/') { @@ -341,54 +299,36 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl) host_changed = TRUE; } else { - /* cut off the original URL from the first slash, or deal with URLs - without slash */ - pathsep = strchr(protsep, '/'); - if(pathsep) { - /* When people use badly formatted URLs, such as - "http://www.example.com?dir=/home/daniel" we must not use the first - slash, if there is a ?-letter before it! */ - char *sep = strchr(protsep, '?'); - if(sep && (sep < pathsep)) - pathsep = sep; + /* cut the original URL at first slash */ + char *pathsep = strchr(protsep, '/'); + if(pathsep) *pathsep = 0; - } - else { - /* There was no slash. Now, since we might be operating on a badly - formatted URL, such as "http://www.example.com?id=2380" which does - not use a slash separator as it is supposed to, we need to check - for a ?-letter as well! */ - pathsep = strchr(protsep, '?'); - if(pathsep) - *pathsep = 0; - } } } + else { + /* the relative piece starts with '#' */ + + /* If there is a fragment in the original URL, cut it off */ + char *pathsep = strchr(protsep, '#'); + if(pathsep) + *pathsep = 0; + } - Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH); + Curl_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH); /* copy over the root URL part */ - result = Curl_dyn_add(&newest, base); + result = Curl_dyn_add(&urlbuf, base); if(result) - return result; - - /* check if we need to append a slash */ - if(('/' == useurl[0]) || (protsep && !*protsep) || skip_slash) - ; - else { - result = Curl_dyn_addn(&newest, "/", 1); - if(result) - return result; - } + return cc2cu(result); /* then append the new piece on the right side */ - uc = urlencode_str(&newest, useurl, strlen(useurl), !host_changed, + uc = urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE); - if(uc) - return (uc == CURLUE_TOO_LARGE) ? CURLE_TOO_LARGE : CURLE_OUT_OF_MEMORY; - - *newurl = Curl_dyn_ptr(&newest); - return CURLE_OK; + if(!uc) + uc = parseurl_and_replace(Curl_dyn_ptr(&urlbuf), u, + flags&~CURLU_PATH_AS_IS); + Curl_dyn_free(&urlbuf); + return uc; } /* scan for byte values <= 31, 127 and sometimes space */ @@ -1856,34 +1796,24 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, * If the existing contents is enough for a URL, allow a relative URL to * replace it. */ - CURLcode result; CURLUcode uc; char *oldurl; - char *redired_url; if(!nalloc) /* a blank URL is not a valid URL */ return CURLUE_MALFORMED_INPUT; - /* if the new thing is absolute or the old one is not - * (we could not get an absolute URL in 'oldurl'), - * then replace the existing with the new. */ + /* if the new thing is absolute or the old one is not (we could not get an + * absolute URL in 'oldurl'), then replace the existing with the new. */ if(Curl_is_absolute_url(part, NULL, 0, - flags & (CURLU_GUESS_SCHEME| - CURLU_DEFAULT_SCHEME)) + flags & (CURLU_GUESS_SCHEME|CURLU_DEFAULT_SCHEME)) || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) { return parseurl_and_replace(part, u, flags); } - /* apply the relative part to create a new URL - * and replace the existing one with it. */ - result = concat_url(oldurl, part, &redired_url); + /* apply the relative part to create a new URL */ + uc = redirect_url(oldurl, part, u, flags); free(oldurl); - if(result) - return cc2cu(result); - - uc = parseurl_and_replace(redired_url, u, flags); - free(redired_url); return uc; } default: diff --git a/lib/urldata.h b/lib/urldata.h index 704fb7a..33fa0d1 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -105,7 +105,7 @@ typedef unsigned int curl_prot_t; #define CURL_DEFAULT_USER "anonymous" #define CURL_DEFAULT_PASSWORD "ftp@example.com" -#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__) +#if !defined(_WIN32) && !defined(MSDOS) /* do FTP line-end CRLF => LF conversions on platforms that prefer LF-only. It also means: keep CRLF line endings on the CRLF platforms */ #define CURL_PREFER_LF_LINEENDS @@ -180,13 +180,6 @@ typedef ssize_t (Curl_recv)(struct Curl_easy *data, /* transfer */ size_t len, /* max amount to read */ CURLcode *err); /* error to return */ -#ifdef USE_HYPER -typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, - struct connectdata *conn, - int *didwhat, - int select_res); -#endif - #include "mime.h" #include "imap.h" #include "pop3.h" @@ -200,7 +193,6 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, #include "mqtt.h" #include "ftplistparser.h" #include "multihandle.h" -#include "c-hyper.h" #include "cf-socket.h" #ifdef HAVE_GSSAPI @@ -271,21 +263,7 @@ enum protection_level { /* SSL backend-specific data; declared differently by each SSL backend */ struct ssl_backend_data; - -typedef enum { - CURL_SSL_PEER_DNS, - CURL_SSL_PEER_IPV4, - CURL_SSL_PEER_IPV6 -} ssl_peer_type; - -struct ssl_peer { - char *hostname; /* hostname for verification */ - char *dispname; /* display version of hostname */ - 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; /* one of TRNSPRT_* defines */ -}; +struct Curl_ssl_scache_entry; struct ssl_primary_config { char *CApath; /* certificate dir (does not work on Windows) */ @@ -337,28 +315,9 @@ struct ssl_config_data { }; struct ssl_general_config { - size_t max_ssl_sessions; /* SSL session id cache size */ int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ }; -typedef void Curl_ssl_sessionid_dtor(void *sessionid, size_t idsize); - -/* information stored about one single SSL session */ -struct Curl_ssl_session { - char *name; /* hostname for which this ID was used */ - char *conn_to_host; /* hostname for the connection (may be NULL) */ - const char *scheme; /* protocol scheme used */ - char *alpn; /* APLN TLS negotiated protocol string */ - void *sessionid; /* as returned from the SSL layer */ - size_t idsize; /* if known, otherwise 0 */ - Curl_ssl_sessionid_dtor *sessionid_free; /* free `sessionid` callback */ - long age; /* just a number, the higher the more recent */ - int remote_port; /* remote port */ - int conn_to_port; /* remote port for the connection (may be -1) */ - int transport; /* TCP or QUIC */ - struct ssl_primary_config ssl_config; /* setup for this session */ -}; - #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" #endif @@ -717,6 +676,12 @@ struct Curl_handler { /* attach() attaches this transfer to this connection */ void (*attach)(struct Curl_easy *data, struct connectdata *conn); + /* return CURLE_OK if a redirect to `newurl` should be followed, + CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change + the way the follow request is performed. */ + CURLcode (*follow)(struct Curl_easy *data, const char *newurl, + followtype type); + int defport; /* Default port. */ curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single specific protocol bit */ @@ -962,10 +927,7 @@ struct connectdata { #ifdef USE_UNIX_SOCKETS char *unix_domain_socket; #endif -#ifdef USE_HYPER - /* if set, an alternative data transfer function */ - Curl_datastream datastream; -#endif + /* When this connection is created, store the conditions for the local end bind. This is stored before the actual bind and before any connection is made and will serve the purpose of being used for comparison reasons so @@ -994,7 +956,9 @@ struct connectdata { #endif unsigned char transport; /* one of the TRNSPRT_* defines */ unsigned char ip_version; /* copied from the Curl_easy at creation time */ - unsigned char httpversion; /* the HTTP version*10 reported by the server */ + /* HTTP version last responded with by the server. + * 0 at start, then one of 09, 10, 11, etc. */ + unsigned char httpversion_seen; unsigned char connect_only; unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ }; @@ -1026,6 +990,8 @@ struct PureInfo { curl_off_t request_size; /* the amount of bytes sent in the request(s) */ unsigned long proxyauthavail; /* what proxy auth types were announced */ unsigned long httpauthavail; /* what host auth types were announced */ + unsigned long proxyauthpicked; /* selected proxy auth type */ + unsigned long httpauthpicked; /* selected host auth type */ long numconnects; /* how many new connection did libcurl created */ char *contenttype; /* the content type of the object */ char *wouldredirect; /* URL this would have been redirected to if asked to */ @@ -1090,6 +1056,7 @@ struct Progress { struct curltime start; struct curltime t_startsingle; struct curltime t_startop; + struct curltime t_startqueue; struct curltime t_acceptdata; #define CURR_TIME (5 + 1) /* 6 entries for 5 seconds */ @@ -1232,8 +1199,7 @@ struct UrlState { curl_prot_t first_remote_protocol; int retrycount; /* number of retries on a new connection */ - struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */ - long sessionage; /* number of the most recent session */ + struct Curl_ssl_scache *ssl_scache; /* TLS session pool */ int os_errno; /* filled in with errno whenever an error occurs */ long followlocation; /* redirect counter */ int requests; /* request counter: redirects + authentication retakes */ @@ -1254,6 +1220,10 @@ struct UrlState { #if defined(USE_OPENSSL) /* void instead of ENGINE to avoid bleeding OpenSSL into this header */ void *engine; + /* this is just a flag -- we do not need to reference the provider in any + * way as OpenSSL takes care of that */ + BIT(provider); + BIT(provider_failed); #endif /* USE_OPENSSL */ struct curltime expiretime; /* set this with Curl_expire() only */ struct Curl_tree timenode; /* for the splay stuff */ @@ -1309,10 +1279,6 @@ struct UrlState { struct curl_slist *cookielist; /* list of cookie files set by curl_easy_setopt(COOKIEFILE) calls */ #endif -#ifdef USE_HYPER - bool hconnect; /* set if a CONNECT request */ - CURLcode hresult; /* used to pass return codes back from hyper callbacks */ -#endif #ifndef CURL_DISABLE_VERBOSE_STRINGS struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */ @@ -1570,23 +1536,15 @@ struct UserDefined { void *out; /* CURLOPT_WRITEDATA */ void *in_set; /* CURLOPT_READDATA */ void *writeheader; /* write the header to this if non-NULL */ - unsigned short use_port; /* which port to use (when not using default) */ unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */ unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */ long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ - void *postfields; /* if POST, set the fields' values here */ curl_seek_callback seek_func; /* function that seeks the input */ curl_off_t postfieldsize; /* if POST, this might have a size to use instead of strlen(), and then the data *may* be binary (contain zero bytes) */ -#ifndef CURL_DISABLE_BINDLOCAL - unsigned short localport; /* local port number to bind to */ - unsigned short localportrange; /* number of additional port numbers to test - in case the 'localport' one cannot be - bind()ed */ -#endif curl_write_callback fwrite_func; /* function that stores the output */ curl_write_callback fwrite_header; /* function that stores headers */ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ @@ -1616,11 +1574,6 @@ struct UserDefined { #endif void *progress_client; /* pointer to pass to the progress callback */ void *ioctl_client; /* pointer to pass to the ioctl callback */ - unsigned int timeout; /* ms, 0 means no timeout */ - unsigned int connecttimeout; /* ms, 0 means default timeout */ - unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */ - unsigned int server_response_timeout; /* ms, 0 means no timeout */ - unsigned int shutdowntimeout; /* ms, 0 means default timeout */ long maxage_conn; /* in seconds, max idle time to allow a connection that is to be reused */ long maxlifetime_conn; /* in seconds, max time since creation to allow a @@ -1648,10 +1601,6 @@ struct UserDefined { struct curl_slist *connect_to; /* list of host:port mappings to override the hostname and port to connect to */ time_t timevalue; /* what time to compare with */ - unsigned char timecondition; /* kind of time comparison: curl_TimeCond */ - unsigned char method; /* what kind of HTTP request: Curl_HttpReq */ - unsigned char httpwant; /* when non-zero, a specific HTTP version requested - to be used in the library's request(s) */ struct ssl_config_data ssl; /* user defined SSL stuff */ #ifndef CURL_DISABLE_PROXY struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ @@ -1671,24 +1620,17 @@ struct UserDefined { #ifndef CURL_DISABLE_HTTP struct curl_slist *http200aliases; /* linked list of aliases for http200 */ #endif - unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header - file 0 - whatever, 1 - v2, 2 - v6 */ curl_off_t max_filesize; /* Maximum file size to download */ #ifndef CURL_DISABLE_FTP + unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */ unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile */ unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */ - unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */ #endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) struct curl_slist *quote; /* after connection is established */ struct curl_slist *postquote; /* after the transfer */ struct curl_slist *prequote; /* before the transfer, after type */ - /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP - 1 - create directories that do not exist - 2 - the same but also allow MKD to fail once - */ - unsigned char ftp_create_missing_dirs; #endif #ifdef USE_LIBSSH2 curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */ @@ -1700,9 +1642,6 @@ struct UserDefined { int ssh_auth_types; /* allowed SSH auth types */ unsigned int new_directory_perms; /* when creating remote dirs */ #endif -#ifndef CURL_DISABLE_NETRC - unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ -#endif unsigned int new_file_perms; /* when creating remote files */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ struct curl_blob *blobs[BLOB_LAST]; @@ -1726,10 +1665,12 @@ struct UserDefined { void *fnmatch_data; void *wildcardptr; #endif - /* GSS-API credential delegation, see the documentation of - CURLOPT_GSSAPI_DELEGATION */ - unsigned char gssapi_delegation; + unsigned int timeout; /* ms, 0 means no timeout */ + unsigned int connecttimeout; /* ms, 0 means default timeout */ + unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */ + unsigned int server_response_timeout; /* ms, 0 means no timeout */ + unsigned int shutdowntimeout; /* ms, 0 means default timeout */ int tcp_keepidle; /* seconds in idle before sending keepalive probe */ int tcp_keepintvl; /* seconds between TCP keepalive probes */ int tcp_keepcnt; /* maximum number of keepalive probes */ @@ -1751,18 +1692,49 @@ struct UserDefined { void *trailer_data; /* pointer to pass to trailer data callback */ curl_trailer_callback trailer_callback; /* trailing data callback */ #endif - char keep_post; /* keep POSTs as POSTs after a 30x request; each - bit represents a request, from 301 to 303 */ #ifndef CURL_DISABLE_SMTP struct curl_slist *mail_rcpt; /* linked list of mail recipients */ - BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some - recipients */ #endif unsigned int maxconnects; /* Max idle connections in the connection cache */ + unsigned short use_port; /* which port to use (when not using default) */ +#ifndef CURL_DISABLE_BINDLOCAL + unsigned short localport; /* local port number to bind to */ + unsigned short localportrange; /* number of additional port numbers to test + in case the 'localport' one cannot be + bind()ed */ +#endif +#ifndef CURL_DISABLE_NETRC + unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ +#endif +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP + 1 - create directories that do not exist + 2 - the same but also allow MKD to fail once + */ + unsigned char ftp_create_missing_dirs; +#endif unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or POP3 or others! (type: curl_usessl)*/ - unsigned char connect_only; /* make connection/request, then let - application use the socket */ + char keep_post; /* keep POSTs as POSTs after a 30x request; each + bit represents a request, from 301 to 303 */ + unsigned char timecondition; /* kind of time comparison: curl_TimeCond */ + unsigned char method; /* what kind of HTTP request: Curl_HttpReq */ + unsigned char httpwant; /* when non-zero, a specific HTTP version requested + to be used in the library's request(s) */ + unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header + file 0 - whatever, 1 - v2, 2 - v6 */ +#ifdef HAVE_GSSAPI + /* GSS-API credential delegation, see the documentation of + CURLOPT_GSSAPI_DELEGATION */ + unsigned char gssapi_delegation; +#endif + BIT(connect_only); /* make connection/request, then let application use the + socket */ + BIT(connect_only_ws); /* special websocket connect-only level */ +#ifndef CURL_DISABLE_SMTP + BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some + recipients */ +#endif #ifndef CURL_DISABLE_MIME BIT(mime_formescape); #endif @@ -1956,9 +1928,6 @@ struct Curl_easy { struct PureInfo info; /* stats, reports and info data */ struct curl_tlssessioninfo tsi; /* Information about the TLS session, only valid after a client has asked for it */ -#ifdef USE_HYPER - struct hyptransfer hyp; -#endif }; #define LIBCURL_NAME "libcurl" diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 2ae6fb3..c06ee10 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -69,7 +69,7 @@ bool Curl_auth_is_digest_supported(void) Curl_pSecFn->FreeContextBuffer(SecurityPackage); } - return (status == SEC_E_OK); + return status == SEC_E_OK; } /* diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index e792782..beaf027 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -42,6 +42,11 @@ #include "curl_memory.h" #include "memdebug.h" +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + /* * Curl_auth_is_gssapi_supported() * @@ -321,4 +326,8 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) } } +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + #endif /* HAVE_GSSAPI && USE_KERBEROS5 */ diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index 4af0bd1..00a5db1 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -64,7 +64,7 @@ bool Curl_auth_is_gssapi_supported(void) Curl_pSecFn->FreeContextBuffer(SecurityPackage); } - return (status == SEC_E_OK); + return status == SEC_E_OK; } /* diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index a2e5bc1..6421c6a 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -63,7 +63,7 @@ bool Curl_auth_is_ntlm_supported(void) Curl_pSecFn->FreeContextBuffer(SecurityPackage); } - return (status == SEC_E_OK); + return status == SEC_E_OK; } /* diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index f48d9b7..55232a8 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -42,6 +42,11 @@ #include "curl_memory.h" #include "memdebug.h" +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + /* * Curl_auth_is_spnego_supported() * @@ -288,4 +293,8 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) nego->havemultiplerequests = FALSE; } +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + #endif /* HAVE_GSSAPI && USE_SPNEGO */ diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 7a27c29..2439e73 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -67,7 +67,7 @@ bool Curl_auth_is_spnego_supported(void) } - return (status == SEC_E_OK); + return status == SEC_E_OK; } /* diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index a7e7e17..171e53f 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -153,10 +153,10 @@ bool Curl_auth_user_contains_domain(const char *user) bool Curl_auth_allowed_to_host(struct Curl_easy *data) { struct connectdata *conn = data->conn; - return (!data->state.this_is_a_follow || - data->set.allow_auth_to_other_hosts || - (data->state.first_host && - strcasecompare(data->state.first_host, conn->host.name) && - (data->state.first_remote_port == conn->remote_port) && - (data->state.first_remote_protocol == conn->handler->protocol))); + return !data->state.this_is_a_follow || + data->set.allow_auth_to_other_hosts || + (data->state.first_host && + strcasecompare(data->state.first_host, conn->host.name) && + (data->state.first_remote_port == conn->remote_port) && + (data->state.first_remote_protocol == conn->handler->protocol)); } diff --git a/lib/version.c b/lib/version.c index 033349b..6543dcd 100644 --- a/lib/version.c +++ b/lib/version.c @@ -38,10 +38,6 @@ #include "easy_lock.h" #ifdef USE_ARES -# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ - defined(_WIN32) -# define CARES_STATICLIB -# endif # include #endif @@ -210,9 +206,6 @@ char *curl_version(void) #ifdef USE_LIBRTMP char rtmp_version[30]; #endif -#ifdef USE_HYPER - char hyper_buf[30]; -#endif #ifdef USE_GSASL char gsasl_buf[30]; #endif @@ -277,10 +270,6 @@ char *curl_version(void) Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); src[i++] = rtmp_version; #endif -#ifdef USE_HYPER - msnprintf(hyper_buf, sizeof(hyper_buf), "Hyper/%s", hyper_version()); - src[i++] = hyper_buf; -#endif #ifdef USE_GSASL msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s", gsasl_check_version(NULL)); @@ -423,10 +412,15 @@ static const char * const supported_protocols[] = { * curl_global_init() and curl_global_cleanup() calls. */ -#if defined(USE_LIBIDN2) +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) static int idn_present(curl_version_info_data *info) { +#if defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) + (void)info; + return TRUE; +#else return info->libidn != NULL; +#endif } #else #define idn_present NULL @@ -468,6 +462,9 @@ static const struct feat features_table[] = { #ifndef CURL_DISABLE_ALTSVC FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC), #endif +#if defined(USE_ARES) && defined(CURLRES_THREADED) && defined(USE_HTTPSRR) + FEATURE("asyn-rr", NULL, 0), +#endif #ifdef CURLRES_ASYNCH FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS), #endif @@ -479,6 +476,10 @@ static const struct feat features_table[] = { #endif #if defined(USE_SSL) && defined(USE_ECH) FEATURE("ECH", ech_present, 0), + +#ifndef USE_HTTPSRR +#error "ECH enabled but not HTTPSRR, must be a config error" +#endif #endif #ifdef USE_GSASL FEATURE("gsasl", NULL, CURL_VERSION_GSASL), @@ -499,6 +500,9 @@ static const struct feat features_table[] = { !defined(CURL_DISABLE_HTTP) FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), #endif +#if defined(USE_HTTPSRR) + FEATURE("HTTPSRR", NULL, 0), +#endif #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) FEATURE("IDN", idn_present, CURL_VERSION_IDN), #endif @@ -530,6 +534,9 @@ static const struct feat features_table[] = { #ifdef USE_SSL FEATURE("SSL", NULL, CURL_VERSION_SSL), #endif +#if defined(USE_SSLS_EXPORT) + FEATURE("SSLS-EXPORT", NULL, 0), +#endif #ifdef USE_WINDOWS_SSPI FEATURE("SSPI", NULL, CURL_VERSION_SSPI), #endif @@ -676,14 +683,6 @@ curl_version_info_data *curl_version_info(CURLversion stamp) } #endif -#ifdef USE_HYPER - { - static char hyper_buffer[30]; - msnprintf(hyper_buffer, sizeof(hyper_buffer), "Hyper/%s", hyper_version()); - version_info.hyper_version = hyper_buffer; - } -#endif - #ifdef USE_GSASL { version_info.gsasl_version = gsasl_check_version(NULL); diff --git a/lib/vquic/curl_msh3.c b/lib/vquic/curl_msh3.c index fe812f8..9e7cfbe 100644 --- a/lib/vquic/curl_msh3.c +++ b/lib/vquic/curl_msh3.c @@ -132,15 +132,23 @@ struct cf_msh3_ctx { static void h3_stream_hash_free(void *stream); -static void cf_msh3_ctx_init(struct cf_msh3_ctx *ctx, - const struct Curl_addrinfo *ai) +static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx, + const struct Curl_addrinfo *ai) { + CURLcode result; + DEBUGASSERT(!ctx->initialized); Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); - Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); + + result = Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); + if(result) + return result; + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; ctx->initialized = TRUE; + + return result; } static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx) @@ -910,7 +918,6 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, if(ctx->handshake_succeeded) { CURL_TRC_CF(data, cf, "handshake succeeded"); cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; cf->connected = TRUE; cf->conn->alpn = CURL_HTTP_VERSION_3; *done = TRUE; @@ -1017,6 +1024,9 @@ static CURLcode cf_msh3_query(struct Curl_cfilter *cf, *when = ctx->handshake_at; return CURLE_OK; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; default: break; } @@ -1039,7 +1049,7 @@ static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_http3 = { "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_msh3_destroy, cf_msh3_connect, @@ -1087,7 +1097,10 @@ CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_msh3_ctx_init(ctx, ai); + + result = cf_msh3_ctx_init(ctx, ai); + if(result) + goto out; result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 37009ab..f2ceff2 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -66,6 +66,7 @@ #include "vquic-tls.h" #include "vtls/keylog.h" #include "vtls/vtls.h" +#include "vtls/vtls_scache.h" #include "curl_ngtcp2.h" #include "warnless.h" @@ -137,8 +138,16 @@ struct cf_ngtcp2_ctx { 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 */ + size_t earlydata_max; /* max amount of early data supported by + server on session reuse */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + * is accepted by peer */ + CURLcode tls_vrfy_result; /* result of TLS peer verification */ int qlogfd; BIT(initialized); + BIT(tls_handshake_complete); /* TLS handshake is done */ + BIT(use_earlydata); /* Using 0RTT data */ + BIT(earlydata_accepted); /* 0RTT was acceptd by server */ BIT(shutdown_started); /* queued shutdown packets */ }; @@ -166,6 +175,8 @@ static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx) static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx) { if(ctx && ctx->initialized) { + Curl_vquic_tls_cleanup(&ctx->tls); + vquic_ctx_free(&ctx->q); Curl_bufcp_free(&ctx->stream_bufcp); Curl_dyn_free(&ctx->scratch); Curl_hash_clean(&ctx->streams); @@ -442,12 +453,45 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, } } -static CURLcode init_ngh3_conn(struct Curl_cfilter *cf); +static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, + struct Curl_easy *data); -static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) +static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) { - (void)user_data; + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; + struct Curl_easy *data; + (void)tconn; + DEBUGASSERT(ctx); + data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(!ctx || !data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + ctx->handshake_at = Curl_now(); + ctx->tls_handshake_complete = TRUE; + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + + ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, + data, &ctx->peer); + CURL_TRC_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(ctx->handshake_at, ctx->started_at)); + /* In case of earlydata, where we simulate being connected, update + * the handshake time when we really did connect */ + if(ctx->use_earlydata) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); +#ifdef USE_GNUTLS + if(ctx->use_earlydata) { + int flags = gnutls_session_get_flags(ctx->tls.gtls.session); + ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA); + CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data", + ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip); + Curl_pgrsEarlyData(data, ctx->earlydata_accepted ? + (curl_off_t)ctx->earlydata_skip : + -(curl_off_t)ctx->earlydata_skip); + } +#endif return 0; } @@ -717,16 +761,19 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, void *user_data) { struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; + struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)tconn; - if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) { + if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) return 0; - } - if(init_ngh3_conn(cf) != CURLE_OK) { - return NGTCP2_ERR_CALLBACK_FAILURE; + DEBUGASSERT(ctx); + DEBUGASSERT(data); + if(ctx && data && !ctx->h3conn) { + if(init_ngh3_conn(cf, data)) + return NGTCP2_ERR_CALLBACK_FAILURE; } - return 0; } @@ -739,7 +786,7 @@ static ngtcp2_callbacks ng_callbacks = { ngtcp2_crypto_client_initial_cb, NULL, /* recv_client_initial */ ngtcp2_crypto_recv_crypto_data_cb, - cb_handshake_completed, + cf_ngtcp2_handshake_completed, NULL, /* recv_version_negotiation */ ngtcp2_crypto_encrypt_cb, ngtcp2_crypto_decrypt_cb, @@ -1128,14 +1175,15 @@ static nghttp3_callbacks ngh3_callbacks = { NULL /* recv_settings */ }; -static CURLcode init_ngh3_conn(struct Curl_cfilter *cf) +static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result; - int rc; int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + int rc; if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { + failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3"); return CURLE_QUIC_CONNECT_ERROR; } @@ -1147,45 +1195,47 @@ static CURLcode init_ngh3_conn(struct Curl_cfilter *cf) nghttp3_mem_default(), cf); if(rc) { - result = CURLE_OUT_OF_MEMORY; - goto fail; + failf(data, "error creating nghttp3 connection instance"); + return CURLE_OUT_OF_MEMORY; } rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; + failf(data, "error creating HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; + failf(data, "error binding HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; + failf(data, "error creating HTTP/3 qpack encoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; + failf(data, "error creating HTTP/3 qpack decoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, qpack_dec_stream_id); if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; + failf(data, "error binding HTTP/3 qpack streams: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } return CURLE_OK; -fail: - - return result; } static ssize_t recv_closed_stream(struct Curl_cfilter *cf, @@ -1236,6 +1286,10 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(ctx->h3conn); *err = CURLE_OK; + /* handshake verification failed in callback, do not recv anything */ + if(ctx->tls_vrfy_result) + return ctx->tls_vrfy_result; + pktx_init(&pktx, cf, data); if(!stream || ctx->shutdown_started) { @@ -1521,7 +1575,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t sent = 0; + ssize_t sent = -1; struct cf_call_data save; struct pkt_io_ctx pktx; CURLcode result; @@ -1533,18 +1587,20 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, pktx_init(&pktx, cf, data); *err = CURLE_OK; + /* handshake verification failed in callback, do not send anything */ + if(ctx->tls_vrfy_result) + return ctx->tls_vrfy_result; + (void)eos; /* TODO: use for stream EOF and block handling */ result = cf_progress_ingress(cf, data, &pktx); if(result) { *err = result; - sent = -1; } if(!stream || stream->id < 0) { if(ctx->shutdown_started) { CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); *err = CURLE_SEND_ERROR; - sent = -1; goto out; } sent = h3_stream_open(cf, data, buf, len, err); @@ -1558,7 +1614,6 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, 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; goto out; } else if(stream->closed) { @@ -1583,7 +1638,6 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, else if(ctx->shutdown_started) { CURL_TRC_CF(data, cf, "cannot send on closed connection"); *err = CURLE_SEND_ERROR; - sent = -1; goto out; } else { @@ -1598,6 +1652,9 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); } + if(sent > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) + ctx->earlydata_skip += sent; + result = cf_progress_egress(cf, data, &pktx); if(result) { *err = result; @@ -1616,17 +1673,6 @@ out: return sent; } -static CURLcode qng_verify_peer(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; - - return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); -} - static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, struct sockaddr_storage *remote_addr, socklen_t remote_addrlen, int ecn, @@ -1696,7 +1742,7 @@ static ssize_t read_pkt_to_send(void *userp, uint32_t flags; int64_t stream_id; int fin; - ssize_t nwritten, n; + ssize_t nwritten = 0, n; veccnt = 0; stream_id = -1; fin = 0; @@ -1708,7 +1754,6 @@ static ssize_t read_pkt_to_send(void *userp, * When ngtcp2 is happy (because it has no other frame that would fit * or it has nothing more to send), it returns the total length * of the assembled packet. This may be 0 if there was nothing to send. */ - nwritten = 0; *err = CURLE_OK; for(;;) { @@ -1946,9 +1991,6 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = h3_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DETACH: - h3_data_done(cf, data); - break; case CF_CTRL_DATA_DONE: h3_data_done(cf, data); break; @@ -2131,7 +2173,8 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) ctx = cf ? cf->ctx : NULL; data = cf ? CF_DATA_CURRENT(cf) : NULL; if(cf && data && ctx) { - Curl_ossl_add_session(cf, data, &ctx->peer, ssl_sessionid); + Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid, + SSL_version(ssl), "h3"); return 1; } return 0; @@ -2139,6 +2182,24 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) #endif /* USE_OPENSSL */ #ifdef USE_GNUTLS + +static const char *gtls_hs_msg_name(int mtype) +{ + switch(mtype) { + case 1: return "ClientHello"; + case 2: return "ServerHello"; + case 4: return "SessionTicket"; + case 8: return "EncryptedExtensions"; + case 11: return "Certificate"; + case 13: return "CertificateRequest"; + case 15: return "CertificateVerify"; + case 20: return "Finished"; + case 24: return "KeyUpdate"; + case 254: return "MessageHash"; + } + return "Unknown"; +} + static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, unsigned when, unsigned int incoming, const gnutls_datum_t *msg) @@ -2152,13 +2213,28 @@ static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, if(when && cf && ctx) { /* after message has been processed */ struct Curl_easy *data = CF_DATA_CURRENT(cf); DEBUGASSERT(data); - if(data) { - CURL_TRC_CF(data, cf, "handshake: %s message type %d", - incoming ? "incoming" : "outgoing", htype); - } + if(!data) + return 0; + CURL_TRC_CF(data, cf, "SSL message: %s %s [%d]", + incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); switch(htype) { case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { - (void)Curl_gtls_update_session_id(cf, data, session, &ctx->peer, "h3"); + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; + + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } + (void)Curl_gtls_cache_session(cf, data, ctx->peer.scache_key, + session, 0, "h3", quic_tp, quic_tp_len); break; } default: @@ -2181,16 +2257,17 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) struct Curl_easy *data = CF_DATA_CURRENT(cf); DEBUGASSERT(data); if(data && ctx) { - (void)wssl_cache_session(cf, data, &ctx->peer, session); + (void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key, + session, wolfSSL_version(ssl), "h3"); } } return 0; } #endif /* USE_WOLFSSL */ -static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - void *user_data) +static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data) { struct curl_tls_ctx *ctx = user_data; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); @@ -2243,6 +2320,53 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, return CURLE_OK; } +static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_ssl_session *scs, + bool *do_early_data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + *do_early_data = FALSE; +#ifdef USE_GNUTLS + ctx->earlydata_max = + gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); + if((!ctx->earlydata_max)) { + CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); + } + else if(strcmp("h3", scs->alpn)) { + CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); + } + else if(!scs->quic_tp || !scs->quic_tp_len) { + CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data, "); + } + else { + int rv; + rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( + ctx->qconn, (uint8_t *)scs->quic_tp, scs->quic_tp_len); + if(rv) + CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " + "parameters: %s", ngtcp2_strerror(rv)); + else { + infof(data, "SSL session allows %zu bytes of early data, " + "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); + result = init_ngh3_conn(cf, data); + if(!result) { + ctx->use_earlydata = TRUE; + cf->connected = TRUE; + *do_early_data = TRUE; + } + } + } +#else /* USE_GNUTLS */ + (void)data; + (void)ctx; + (void)scs; +#endif + return result; +} + /* * Might be called twice for happy eyeballs. */ @@ -2258,21 +2382,6 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, int qfd; DEBUGASSERT(ctx->initialized); - result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); - if(result) - return result; - -#define H3_ALPN "\x2h3\x5h3-29" - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, - H3_ALPN, sizeof(H3_ALPN) - 1, - tls_ctx_setup, &ctx->tls, &ctx->conn_ref); - if(result) - return result; - -#ifdef USE_OPENSSL - SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); -#endif - ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); if(result) @@ -2314,7 +2423,17 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, if(rc) return CURLE_QUIC_CONNECT_ERROR; +#define H3_ALPN "\x2h3\x5h3-29" + result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, + H3_ALPN, sizeof(H3_ALPN) - 1, + cf_ngtcp2_tls_ctx_setup, &ctx->tls, + &ctx->conn_ref, + cf_ngtcp2_on_session_reuse); + if(result) + return result; + #ifdef USE_OPENSSL + SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); #elif defined(USE_GNUTLS) ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); @@ -2365,6 +2484,11 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, result = cf_connect_start(cf, data, &pktx); if(result) goto out; + if(cf->connected) { + cf->conn->alpn = CURL_HTTP_VERSION_3; + *done = TRUE; + goto out; + } result = cf_progress_egress(cf, data, &pktx); /* we do not expect to be able to recv anything yet */ goto out; @@ -2379,10 +2503,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, goto out; if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { - ctx->handshake_at = now; - CURL_TRC_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at)); - result = qng_verify_peer(cf, data); + result = ctx->tls_vrfy_result; if(!result) { CURL_TRC_CF(data, cf, "peer verified"); cf->connected = TRUE; @@ -2474,6 +2595,9 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, *when = ctx->handshake_at; return CURLE_OK; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; default: break; } @@ -2536,7 +2660,7 @@ out: struct Curl_cftype Curl_cft_http3 = { "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_ngtcp2_destroy, cf_ngtcp2_connect, diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index e5f737f..1b78497 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -292,6 +292,9 @@ struct cf_osslq_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 */ + SSL_POLL_ITEM *poll_items; /* Array for polling on writable state */ + struct Curl_easy **curl_items; /* Array of easy objs */ + size_t item_count; /* count of elements in poll/curl_items */ BIT(initialized); BIT(got_first_byte); /* if first byte was received */ BIT(x509_store_setup); /* if x509 store has been set up */ @@ -308,6 +311,9 @@ static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx) 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->poll_items = NULL; + ctx->curl_items = NULL; + ctx->item_count = 0; ctx->initialized = TRUE; } @@ -318,6 +324,8 @@ static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx) Curl_hash_clean(&ctx->streams); Curl_hash_destroy(&ctx->streams); Curl_ssl_peer_cleanup(&ctx->peer); + free(ctx->poll_items); + free(ctx->curl_items); } free(ctx); } @@ -562,7 +570,6 @@ static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx; cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -1162,14 +1169,11 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, BIO_ADDR *baddr = NULL; DEBUGASSERT(ctx->initialized); - result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); - if(result) - goto out; #define H3_ALPN "\x2h3" result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, H3_ALPN, sizeof(H3_ALPN) - 1, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); if(result) goto out; @@ -1464,24 +1468,80 @@ static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, { struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream; + size_t poll_count = 0; + size_t result_count = 0; + size_t idx_count = 0; + CURLcode res = CURLE_OK; + struct timeval timeout; + void *tmpptr; if(ctx->h3.conn) { struct Curl_llist_node *e; + + res = CURLE_OUT_OF_MEMORY; + + if(ctx->item_count < Curl_llist_count(&data->multi->process)) { + ctx->item_count = 0; + tmpptr = realloc(ctx->poll_items, + Curl_llist_count(&data->multi->process) * + sizeof(SSL_POLL_ITEM)); + if(!tmpptr) { + free(ctx->poll_items); + ctx->poll_items = NULL; + goto out; + } + ctx->poll_items = tmpptr; + + tmpptr = realloc(ctx->curl_items, + Curl_llist_count(&data->multi->process) * + sizeof(struct Curl_easy *)); + if(!tmpptr) { + free(ctx->curl_items); + ctx->curl_items = NULL; + goto out; + } + ctx->curl_items = tmpptr; + + ctx->item_count = Curl_llist_count(&data->multi->process); + } + 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 && - !SSL_want_write(stream->s.ssl)) { - nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id); - stream->s.send_blocked = FALSE; - h3_drain_stream(cf, sdata); - CURL_TRC_CF(sdata, cf, "unblocked"); + if(stream && stream->s.ssl && stream->s.send_blocked) { + ctx->poll_items[poll_count].desc = + SSL_as_poll_descriptor(stream->s.ssl); + ctx->poll_items[poll_count].events = SSL_POLL_EVENT_W; + ctx->curl_items[poll_count] = sdata; + poll_count++; } } } + + memset(&timeout, 0, sizeof(struct timeval)); + res = CURLE_UNRECOVERABLE_POLL; + if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM), &timeout, + 0, &result_count)) + goto out; + + res = CURLE_OK; + + for(idx_count = 0; idx_count < poll_count && result_count > 0; + idx_count++) { + if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) { + stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]); + nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id); + stream->s.send_blocked = FALSE; + h3_drain_stream(cf, ctx->curl_items[idx_count]); + CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked"); + result_count--; + } + } } - return CURLE_OK; + +out: + return res; } static CURLcode h3_send_streams(struct Curl_cfilter *cf, @@ -2139,9 +2199,6 @@ static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = h3_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DETACH: - h3_data_done(cf, data); - break; case CF_CTRL_DATA_DONE: h3_data_done(cf, data); break; @@ -2301,6 +2358,9 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf, *when = ctx->handshake_at; return CURLE_OK; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; default: break; } @@ -2311,7 +2371,7 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_http3 = { "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_osslq_destroy, cf_osslq_connect, diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 3048498..ec0dcce 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -1228,9 +1228,6 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = h3_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DETACH: - h3_data_done(cf, data); - break; case CF_CTRL_DATA_DONE: h3_data_done(cf, data); break; @@ -1278,10 +1275,6 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, if(result) return result; - result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); - if(result) - return result; - ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); if(!ctx->cfg) { failf(data, "cannot create quiche config"); @@ -1313,7 +1306,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, QUICHE_H3_APPLICATION_PROTOCOL, sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1, - NULL, NULL, cf); + NULL, NULL, cf, NULL); if(result) return result; @@ -1379,7 +1372,6 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -1573,6 +1565,9 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, *when = ctx->handshake_at; return CURLE_OK; } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; default: break; } @@ -1620,7 +1615,7 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_http3 = { "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 0, cf_quiche_destroy, cf_quiche_connect, diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index 580e570..6e1ace2 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -50,6 +50,7 @@ #include "multiif.h" #include "vtls/keylog.h" #include "vtls/vtls.h" +#include "vtls/vtls_scache.h" #include "vquic-tls.h" /* The last 3 #include files should be in this order */ @@ -221,7 +222,7 @@ static CURLcode wssl_init_ssl(struct curl_tls_ctx *ctx, } if(ssl_config->primary.cache_session) { - (void)wssl_setup_session(cf, data, &ctx->wssl, peer); + (void)Curl_wssl_setup_session(cf, data, &ctx->wssl, peer->scache_key); } return CURLE_OK; @@ -234,25 +235,43 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct ssl_peer *peer, const char *alpn, size_t alpn_len, Curl_vquic_tls_ctx_setup *cb_setup, - void *cb_user_data, void *ssl_user_data) + void *cb_user_data, void *ssl_user_data, + Curl_vquic_session_reuse_cb *session_reuse_cb) { + char tls_id[80]; CURLcode result; #ifdef USE_OPENSSL + Curl_ossl_version(tls_id, sizeof(tls_id)); +#elif defined(USE_GNUTLS) + Curl_gtls_version(tls_id, sizeof(tls_id)); +#elif defined(USE_WOLFSSL) + Curl_wssl_version(tls_id, sizeof(tls_id)); +#else +#error "no TLS lib in used, should not happen" + return CURLE_FAILED_INIT; +#endif + (void)session_reuse_cb; + result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC); + if(result) + return result; + +#ifdef USE_OPENSSL (void)result; - return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC, + return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, (const unsigned char *)alpn, alpn_len, cb_setup, cb_user_data, NULL, ssl_user_data); #elif defined(USE_GNUTLS) - (void)result; return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, - (const unsigned char *)alpn, alpn_len, NULL, - cb_setup, cb_user_data, ssl_user_data); + (const unsigned char *)alpn, alpn_len, + cb_setup, cb_user_data, ssl_user_data, + session_reuse_cb); #elif defined(USE_WOLFSSL) result = wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); if(result) return result; + (void)session_reuse_cb; return wssl_init_ssl(ctx, cf, data, peer, alpn, alpn_len, ssl_user_data); #else #error "no TLS lib in used, should not happen" @@ -346,6 +365,9 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, } #endif + /* on error, remove any session we might have in the pool */ + if(result) + Curl_ssl_scache_remove_all(cf, data, peer->scache_key); return result; } diff --git a/lib/vquic/vquic-tls.h b/lib/vquic/vquic-tls.h index 969acad..c0706b0 100644 --- a/lib/vquic/vquic-tls.h +++ b/lib/vquic/vquic-tls.h @@ -26,6 +26,7 @@ #include "curl_setup.h" #include "bufq.h" +#include "vtls/vtls.h" #include "vtls/openssl.h" #if defined(USE_HTTP3) && \ @@ -33,6 +34,9 @@ #include "vtls/wolfssl.h" +struct ssl_peer; +struct Curl_ssl_session; + struct curl_tls_ctx { #ifdef USE_OPENSSL struct ossl_ctx ossl; @@ -54,6 +58,11 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, struct Curl_easy *data, void *cb_user_data); +typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_ssl_session *scs, + bool *do_early_data); + /** * Initialize the QUIC TLS instances based of the SSL configurations * for the connection filter, transfer and peer. @@ -65,8 +74,9 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, * may be NULL * @param alpn_len the overall number of bytes in `alpn` * @param cb_setup optional callback for early TLS config - ± @param cb_user_data user_data param for callback + * @param cb_user_data user_data param for callback * @param ssl_user_data optional pointer to set in TLS application context + * @param session_reuse_cb callback to handle session reuse, signal early data */ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, @@ -75,7 +85,8 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, const char *alpn, size_t alpn_len, Curl_vquic_tls_ctx_setup *cb_setup, void *cb_user_data, - void *ssl_user_data); + void *ssl_user_data, + Curl_vquic_session_reuse_cb *session_reuse_cb); /** * Cleanup all data that has been initialized. diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 20ff6a6..9ac3518 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -53,12 +53,6 @@ #ifdef USE_HTTP3 -#ifdef O_BINARY -#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY -#else -#define QLOGMODE O_WRONLY|O_CREAT -#endif - #define NW_CHUNK_SIZE (64 * 1024) #define NW_SEND_CHUNKS 2 @@ -248,6 +242,7 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, /* simulate network blocking/partial writes */ if(qctx->wblock_percent > 0) { unsigned char c; + *psent = 0; Curl_rand(data, &c, 1); if(c >= ((100-qctx->wblock_percent)*256/100)) { CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK"); @@ -363,7 +358,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct mmsghdr mmsg[MMSG_NUM]; uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))]; struct sockaddr_storage remote_addr[MMSG_NUM]; - size_t total_nread = 0, pkts; + size_t total_nread = 0, pkts = 0; int mcount, i, n; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; @@ -380,7 +375,6 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, goto out; bufs = (uint8_t (*)[64*1024])sockbuf; - pkts = 0; total_nread = 0; while(pkts < max_pkts) { n = (int)CURLMIN(MMSG_NUM, max_pkts); @@ -657,7 +651,7 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, result = Curl_dyn_add(&fname, ".sqlog"); if(!result) { - int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE, + int qlogfd = open(Curl_dyn_ptr(&fname), O_WRONLY|O_CREAT|CURL_O_BINARY, data->set.new_file_perms); if(qlogfd != -1) *qlogfdp = qlogfd; @@ -695,24 +689,6 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, #endif } -bool Curl_conn_is_http3(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ -#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_conn_is_ngtcp2(data, conn, sockindex); -#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) - return Curl_conn_is_osslq(data, conn, sockindex); -#elif defined(USE_QUICHE) - return Curl_conn_is_quiche(data, conn, sockindex); -#elif defined(USE_MSH3) - return Curl_conn_is_msh3(data, conn, sockindex); -#else - return ((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (conn->httpversion == 30)); -#endif -} - CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn) { diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index c1ca1df..1cd3e25 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -46,16 +46,8 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, const struct Curl_addrinfo *ai, int transport); -bool Curl_conn_is_http3(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); - extern struct Curl_cftype Curl_cft_http3; -#else /* USE_HTTP3 */ - -#define Curl_conn_is_http3(a,b,c) FALSE - #endif /* !USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 6bae061..8d42ddd 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -161,6 +161,7 @@ const struct Curl_handler Curl_handler_scp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_SSH, /* defport */ CURLPROTO_SCP, /* protocol */ CURLPROTO_SCP, /* family */ @@ -189,6 +190,7 @@ const struct Curl_handler Curl_handler_sftp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_SSH, /* defport */ CURLPROTO_SFTP, /* protocol */ CURLPROTO_SFTP, /* family */ diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index e19ffef..edfadc8 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -139,6 +139,7 @@ const struct Curl_handler Curl_handler_scp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ssh_attach, /* attach */ + ZERO_NULL, /* follow */ PORT_SSH, /* defport */ CURLPROTO_SCP, /* protocol */ CURLPROTO_SCP, /* family */ @@ -169,6 +170,7 @@ const struct Curl_handler Curl_handler_sftp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ssh_attach, /* attach */ + ZERO_NULL, /* follow */ PORT_SSH, /* defport */ CURLPROTO_SFTP, /* protocol */ CURLPROTO_SFTP, /* family */ diff --git a/lib/vssh/wolfssh.c b/lib/vssh/wolfssh.c index 5400821..e78a18e 100644 --- a/lib/vssh/wolfssh.c +++ b/lib/vssh/wolfssh.c @@ -95,6 +95,7 @@ const struct Curl_handler Curl_handler_scp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_SSH, /* defport */ CURLPROTO_SCP, /* protocol */ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION @@ -125,6 +126,7 @@ const struct Curl_handler Curl_handler_sftp = { ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ PORT_SSH, /* defport */ CURLPROTO_SFTP, /* protocol */ CURLPROTO_SFTP, /* family */ diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c index 53fd4a6..c52f28d 100644 --- a/lib/vtls/bearssl.c +++ b/lib/vtls/bearssl.c @@ -34,6 +34,7 @@ #include "inet_pton.h" #include "vtls.h" #include "vtls_int.h" +#include "vtls_scache.h" #include "connect.h" #include "select.h" #include "multiif.h" @@ -609,20 +610,19 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); if(ssl_config->primary.cache_session) { - void *sdata; - size_t slen; + struct Curl_ssl_session *sc_session = NULL; const br_ssl_session_parameters *session; - CURL_TRC_CF(data, cf, "connect_step1, check session cache"); - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &sdata, &slen, NULL) && - slen == sizeof(*session)) { - session = sdata; + ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key, + &sc_session); + if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) { + session = (br_ssl_session_parameters *)(void *)sc_session->sdata; br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); session_set = 1; infof(data, "BearSSL: reusing session ID"); + /* single use of sessions */ + Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session); } - Curl_ssl_sessionid_unlock(data); } if(connssl->alpn) { @@ -804,12 +804,6 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, return ret; } -static void bearssl_session_free(void *sessionid, size_t idsize) -{ - (void)idsize; - free(sessionid); -} - static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -832,17 +826,22 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, } if(ssl_config->primary.cache_session) { + struct Curl_ssl_session *sc_session; br_ssl_session_parameters *session; session = malloc(sizeof(*session)); if(!session) return CURLE_OUT_OF_MEMORY; br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); - Curl_ssl_sessionid_lock(data); - ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, - session, sizeof(*session), - bearssl_session_free); - Curl_ssl_sessionid_unlock(data); + ret = Curl_ssl_session_create((unsigned char *)session, sizeof(*session), + (int)session->version, + connssl->negotiated.alpn, + 0, 0, &sc_session); + if(!ret) { + ret = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key, + sc_session); + /* took ownership of `sc_session` */ + } if(ret) return ret; } @@ -1154,27 +1153,24 @@ const struct Curl_ssl Curl_ssl_bearssl = { sizeof(struct bearssl_ssl_backend_data), - Curl_none_init, /* init */ - Curl_none_cleanup, /* cleanup */ + NULL, /* init */ + NULL, /* cleanup */ bearssl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ bearssl_shutdown, /* shutdown */ bearssl_data_pending, /* data_pending */ bearssl_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ + NULL, /* cert_status_request */ bearssl_connect, /* connect */ bearssl_connect_nonblocking, /* connect_nonblocking */ Curl_ssl_adjust_pollset, /* adjust_pollset */ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ + NULL, /* close_all */ + NULL, /* set_engine */ + NULL, /* set_engine_default */ + NULL, /* engines_list */ + NULL, /* false_start */ bearssl_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ bearssl_recv, /* recv decrypted data */ bearssl_send, /* send data to encrypt */ NULL, /* get_channel_binding */ diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index af4f0c3..d2c0172 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -47,12 +47,14 @@ #include "gtls.h" #include "vtls.h" #include "vtls_int.h" +#include "vtls_scache.h" #include "vauth/vauth.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ #include "progress.h" #include "select.h" #include "strcase.h" +#include "strdup.h" #include "warnless.h" #include "x509asn1.h" #include "multiif.h" @@ -714,21 +716,20 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, return CURLE_OK; } -static void gtls_sessionid_free(void *sessionid, size_t idsize) -{ - (void)idsize; - free(sessionid); -} - -CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf, - struct Curl_easy *data, - gnutls_session_t session, - struct ssl_peer *peer, - const char *alpn) +CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + gnutls_session_t session, + curl_off_t valid_until, + const char *alpn, + unsigned char *quic_tp, + size_t quic_tp_len) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - void *connect_sessionid; - size_t connect_idsize = 0; + struct Curl_ssl_session *sc_session; + unsigned char *sdata, *qtp_clone = NULL; + size_t sdata_len = 0; + size_t earlydata_max = 0; CURLcode result = CURLE_OK; if(!ssl_config->primary.cache_session) @@ -740,35 +741,68 @@ CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf, detect that. */ /* get the session ID data size */ - gnutls_session_get_data(session, NULL, &connect_idsize); - if(!connect_idsize) /* gnutls does this for some version combinations */ + gnutls_session_get_data(session, NULL, &sdata_len); + if(!sdata_len) /* gnutls does this for some version combinations */ return CURLE_OK; - connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ - if(!connect_sessionid) + sdata = malloc(sdata_len); /* get a buffer for it */ + if(!sdata) return CURLE_OUT_OF_MEMORY; /* extract session ID to the allocated buffer */ - gnutls_session_get_data(session, connect_sessionid, &connect_idsize); - - CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s) and store in cache", - connect_idsize, alpn ? alpn : "-"); - Curl_ssl_sessionid_lock(data); - /* store this session id, takes ownership */ - result = Curl_ssl_set_sessionid(cf, data, peer, alpn, - connect_sessionid, connect_idsize, - gtls_sessionid_free); - Curl_ssl_sessionid_unlock(data); + gnutls_session_get_data(session, sdata, &sdata_len); + earlydata_max = gnutls_record_get_max_early_data_size(session); + + CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s, earlymax=%zu) " + "and store in cache", sdata_len, alpn ? alpn : "-", + earlydata_max); + if(quic_tp && quic_tp_len) { + qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len); + if(!qtp_clone) { + free(sdata); + return CURLE_OUT_OF_MEMORY; + } + } + + result = Curl_ssl_session_create2(sdata, sdata_len, + Curl_glts_get_ietf_proto(session), + alpn, valid_until, earlydata_max, + qtp_clone, quic_tp_len, + &sc_session); + /* call took ownership of `sdata` and `qtp_clone` */ + if(!result) { + result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session); + /* took ownership of `sc_session` */ + } return result; } +int Curl_glts_get_ietf_proto(gnutls_session_t session) +{ + switch(gnutls_protocol_get_version(session)) { + case GNUTLS_SSL3: + return CURL_IETF_PROTO_SSL3; + case GNUTLS_TLS1_0: + return CURL_IETF_PROTO_TLS1; + case GNUTLS_TLS1_1: + return CURL_IETF_PROTO_TLS1_1; + case GNUTLS_TLS1_2: + return CURL_IETF_PROTO_TLS1_2; + case GNUTLS_TLS1_3: + return CURL_IETF_PROTO_TLS1_3; + default: + return CURL_IETF_PROTO_UNKNOWN; + } +} + static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf, struct Curl_easy *data, gnutls_session_t session) { struct ssl_connect_data *connssl = cf->ctx; - return Curl_gtls_update_session_id(cf, data, session, &connssl->peer, - connssl->alpn_negotiated); + return Curl_gtls_cache_session(cf, data, connssl->peer.scache_key, + session, 0, connssl->negotiated.alpn, + NULL, 0); } static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, @@ -800,6 +834,7 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, static CURLcode gtls_client_init(struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, + size_t earlydata_max, struct gtls_ctx *gtls) { struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf); @@ -853,6 +888,14 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, /* Initialize TLS session as a client */ init_flags = GNUTLS_CLIENT; + if(peer->transport == TRNSPRT_QUIC && earlydata_max > 0) + init_flags |= GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_END_OF_EARLY_DATA; + else if(earlydata_max > 0 && earlydata_max != 0xFFFFFFFFUL) + /* See https://gitlab.com/gnutls/gnutls/-/issues/1619 + * We cannot differentiate between a session announcing no earldata + * and one announcing 0xFFFFFFFFUL. On TCP+TLS, this is unlikely, but + * on QUIC this is common. */ + init_flags |= GNUTLS_ENABLE_EARLY_DATA; #if defined(GNUTLS_FORCE_CLIENT_CERT) init_flags |= GNUTLS_FORCE_CLIENT_CERT; @@ -874,6 +917,8 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, init_flags |= GNUTLS_NO_STATUS_REQUEST; #endif + CURL_TRC_CF(data, cf, "gnutls_init(flags=%x), earlydata=%zu", + init_flags, earlydata_max); rc = gnutls_init(>ls->session, init_flags); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_init() failed: %d", rc); @@ -1046,96 +1091,129 @@ static int keylog_callback(gnutls_session_t session, const char *label, return 0; } +static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_ssl_session *scs, + bool *do_early_data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; + + *do_early_data = FALSE; + connssl->earlydata_max = + gnutls_record_get_max_early_data_size(backend->gtls.session); + if((!connssl->earlydata_max || connssl->earlydata_max == 0xFFFFFFFFUL)) { + /* Seems to be GnuTLS way to signal no EarlyData in session */ + CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); + } + else if(!Curl_alpn_contains_proto(connssl->alpn, scs->alpn)) { + CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); + } + else { + infof(data, "SSL session allows %zu bytes of early data, " + "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); + connssl->earlydata_state = ssl_earlydata_use; + connssl->state = ssl_connection_deferred; + result = Curl_alpn_set_negotiated(cf, data, connssl, + (const unsigned char *)scs->alpn, + scs->alpn ? strlen(scs->alpn) : 0); + *do_early_data = !result; + } + return result; +} + CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, const unsigned char *alpn, size_t alpn_len, - struct ssl_connect_data *connssl, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, - void *ssl_user_data) + void *ssl_user_data, + Curl_gtls_init_session_reuse_cb *sess_reuse_cb) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct Curl_ssl_session *scs = NULL; gnutls_datum_t gtls_alpns[5]; size_t gtls_alpns_count = 0; + bool gtls_session_setup = FALSE; CURLcode result; + int rc; DEBUGASSERT(gctx); - - result = gtls_client_init(cf, data, peer, gctx); - if(result) - return result; - - gnutls_session_set_ptr(gctx->session, ssl_user_data); - - if(cb_setup) { - result = cb_setup(cf, data, cb_user_data); + /* This might be a reconnect, so we check for a session ID in the cache + to speed up things. We need to do this before constructing the gnutls + session since we need to set flags depending on the kind of reuse. */ + if(conn_config->cache_session) { + result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs); if(result) - return result; - } + goto out; - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - gnutls_session_set_keylog_function(gctx->session, keylog_callback); - } + if(scs && scs->sdata && scs->sdata_len) { + /* we got a cached session, use it! */ - /* This might be a reconnect, so we check for a session ID in the cache - to speed up things */ - if(conn_config->cache_session) { - void *ssl_sessionid; - size_t ssl_idsize; - char *session_alpn; - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, peer, - &ssl_sessionid, &ssl_idsize, &session_alpn)) { - /* we got a session id, use it! */ - int rc; - - rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize); + result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx); + if(result) + goto out; + gtls_session_setup = TRUE; + + rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len); if(rc < 0) - infof(data, "SSL failed to set session ID"); + infof(data, "SSL session not accepted by GnuTLS, continuing without"); else { - infof(data, "SSL reusing session ID (size=%zu, alpn=%s)", - ssl_idsize, session_alpn ? session_alpn : "-"); -#ifdef DEBUGBUILD - if((ssl_config->earlydata || !!getenv("CURL_USE_EARLYDATA")) && -#else + infof(data, "SSL reusing session with ALPN '%s'", + scs->alpn ? scs->alpn : "-"); if(ssl_config->earlydata && -#endif - !cf->conn->connect_only && connssl && - (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3) && - Curl_alpn_contains_proto(connssl->alpn, session_alpn)) { - connssl->earlydata_max = - gnutls_record_get_max_early_data_size(gctx->session); - if((!connssl->earlydata_max || - connssl->earlydata_max == 0xFFFFFFFFUL)) { - /* Seems to be GnuTLS way to signal no EarlyData in session */ - CURL_TRC_CF(data, cf, "TLS session does not allow earlydata"); - } - else { - CURL_TRC_CF(data, cf, "TLS session allows %zu earlydata bytes, " - "reusing ALPN '%s'", - connssl->earlydata_max, session_alpn); - connssl->earlydata_state = ssl_earlydata_use; - connssl->state = ssl_connection_deferred; - result = Curl_alpn_set_negotiated(cf, data, connssl, - (const unsigned char *)session_alpn, - session_alpn ? strlen(session_alpn) : 0); + !cf->conn->connect_only && + (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3)) { + bool do_early_data = FALSE; + if(sess_reuse_cb) { + result = sess_reuse_cb(cf, data, scs, &do_early_data); if(result) - return result; + goto out; + } + if(do_early_data) { /* We only try the ALPN protocol the session used before, * otherwise we might send early data for the wrong protocol */ - gtls_alpns[0].data = (unsigned char *)session_alpn; - gtls_alpns[0].size = (unsigned)strlen(session_alpn); - gtls_alpns_count = 1; + gtls_alpns[0].data = (unsigned char *)scs->alpn; + gtls_alpns[0].size = (unsigned)strlen(scs->alpn); + if(gnutls_alpn_set_protocols(gctx->session, + gtls_alpns, 1, + GNUTLS_ALPN_MANDATORY)) { + failf(data, "failed setting ALPN"); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + /* don't set again below */ + gtls_alpns_count = 0; + alpn = NULL; } } } } - Curl_ssl_sessionid_unlock(data); + } + + if(!gtls_session_setup) { + result = gtls_client_init(cf, data, peer, 0, gctx); + if(result) + goto out; + } + + gnutls_session_set_ptr(gctx->session, ssl_user_data); + + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); + if(result) + goto out; + } + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + gnutls_session_set_keylog_function(gctx->session, keylog_callback); } /* convert the ALPN string from our arguments to a list of strings that @@ -1143,19 +1221,21 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, * to the server. nice. */ if(!gtls_alpns_count && alpn && alpn_len) { size_t i, alen = alpn_len; - unsigned char *s = (unsigned char *)alpn; + unsigned char *salpn = (unsigned char *)alpn; unsigned char slen; for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) { - slen = s[0]; + slen = salpn[0]; if(slen >= alen) return CURLE_FAILED_INIT; - gtls_alpns[i].data = s + 1; + gtls_alpns[i].data = salpn + 1; gtls_alpns[i].size = slen; - s += slen + 1; + salpn += slen + 1; alen -= (size_t)slen + 1; } - if(alen) /* not all alpn chars used, wrong format or too many */ - return CURLE_FAILED_INIT; + if(alen) { /* not all alpn chars used, wrong format or too many */ + result = CURLE_FAILED_INIT; + goto out; + } gtls_alpns_count = i; } @@ -1164,10 +1244,12 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, gtls_alpns, (unsigned int)gtls_alpns_count, GNUTLS_ALPN_MANDATORY)) { failf(data, "failed setting ALPN"); - return CURLE_SSL_CONNECT_ERROR; + result = CURLE_SSL_CONNECT_ERROR; } - return CURLE_OK; +out: + Curl_ssl_scache_return(cf, data, peer->scache_key, scs); + return result; } static CURLcode @@ -1197,7 +1279,8 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer, - proto.data, proto.len, connssl, NULL, NULL, cf); + proto.data, proto.len, + NULL, NULL, cf, gtls_on_session_reuse); if(result) return result; @@ -1886,6 +1969,9 @@ gtls_connect_common(struct Curl_cfilter *cf, goto out; if(connssl->earlydata_state == ssl_earlydata_sent) { + /* report the true time the handshake was done */ + connssl->handshake_done = Curl_now(); + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); if(gnutls_session_get_flags(backend->gtls.session) & GNUTLS_SFLAGS_EARLY_DATA) { connssl->earlydata_state = ssl_earlydata_accepted; @@ -2213,7 +2299,7 @@ out: return ret; } -static size_t gtls_version(char *buffer, size_t size) +size_t Curl_gtls_version(char *buffer, size_t size) { return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); } @@ -2268,8 +2354,7 @@ const struct Curl_ssl Curl_ssl_gnutls = { gtls_init, /* init */ gtls_cleanup, /* cleanup */ - gtls_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ + Curl_gtls_version, /* version */ gtls_shutdown, /* shutdown */ gtls_data_pending, /* data_pending */ gtls_random, /* random */ @@ -2279,14 +2364,12 @@ const struct Curl_ssl Curl_ssl_gnutls = { Curl_ssl_adjust_pollset, /* adjust_pollset */ gtls_get_internals, /* get_internals */ gtls_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ + NULL, /* close_all */ + NULL, /* set_engine */ + NULL, /* set_engine_default */ + NULL, /* engines_list */ + NULL, /* false_start */ gtls_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ gtls_recv, /* recv decrypted data */ gtls_send, /* send data to encrypt */ NULL, /* get_channel_binding */ diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index 4f9c540..a17dcd7 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -46,6 +46,9 @@ struct ssl_primary_config; struct ssl_config_data; struct ssl_peer; struct ssl_connect_data; +struct Curl_ssl_session; + +int Curl_glts_get_ietf_proto(gnutls_session_t session); struct gtls_shared_creds { gnutls_certificate_credentials_t creds; @@ -70,19 +73,26 @@ struct gtls_ctx { BIT(sent_shutdown); }; +size_t Curl_gtls_version(char *buffer, size_t size); + typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf, struct Curl_easy *data, void *user_data); +typedef CURLcode Curl_gtls_init_session_reuse_cb(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_ssl_session *scs, + bool *do_early_data); + CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, const unsigned char *alpn, size_t alpn_len, - struct ssl_connect_data *connssl, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, - void *ssl_user_data); + void *ssl_user_data, + Curl_gtls_init_session_reuse_cb *sess_reuse_cb); CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -96,11 +106,14 @@ CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, const char *pinned_key); /* Extract TLS session and place in cache, if configured. */ -CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf, - struct Curl_easy *data, - gnutls_session_t session, - struct ssl_peer *peer, - const char *alpn); +CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + gnutls_session_t session, + curl_off_t valid_until, + const char *alpn, + unsigned char *quic_tp, + size_t quic_tp_len); extern const struct Curl_ssl Curl_ssl_gnutls; diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index e071ded..456b561 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -64,6 +64,7 @@ #include "mbedtls.h" #include "vtls.h" #include "vtls_int.h" +#include "vtls_scache.h" #include "x509asn1.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ @@ -100,29 +101,36 @@ struct mbed_ssl_backend_data { const char *protocols[3]; #endif int *ciphersuites; + size_t send_blocked_len; BIT(initialized); /* mbedtls_ssl_context is initialized */ BIT(sent_shutdown); + BIT(send_blocked); }; /* apply threading? */ -#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - defined(_WIN32) -#define THREADING_SUPPORT +#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ + defined(_WIN32) +#define HAS_THREADING_SUPPORT #endif #ifndef MBEDTLS_ERROR_C #define mbedtls_strerror(a,b,c) b[0] = 0 #endif +/* PSA can be used independently of TLS 1.3 */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) && MBEDTLS_VERSION_NUMBER >= 0x03060000 +#define HAS_PSA_SUPPORT +#endif + #if defined(MBEDTLS_SSL_PROTO_TLS1_3) && MBEDTLS_VERSION_NUMBER >= 0x03060000 -#define TLS13_SUPPORT +#define HAS_TLS13_SUPPORT #endif -#if defined(TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#if defined(HAS_TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS) #define HAS_SESSION_TICKETS #endif -#ifdef THREADING_SUPPORT +#ifdef HAS_THREADING_SUPPORT static mbedtls_entropy_context ts_entropy; static int entropy_init_initialized = 0; @@ -160,7 +168,7 @@ static int entropy_func_mutex(void *data, unsigned char *output, size_t len) return ret; } -#endif /* THREADING_SUPPORT */ +#endif /* HAS_THREADING_SUPPORT */ #ifdef MBEDTLS_DEBUG static void mbed_debug(void *context, int level, const char *f_name, @@ -290,7 +298,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, /* ver_min = MBEDTLS_SSL_VERSION_TLS1_2; */ break; case CURL_SSLVERSION_TLSv1_3: -#ifdef TLS13_SUPPORT +#ifdef HAS_TLS13_SUPPORT ver_min = MBEDTLS_SSL_VERSION_TLS1_3; break; #endif @@ -304,7 +312,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, case CURL_SSLVERSION_MAX_DEFAULT: case CURL_SSLVERSION_MAX_NONE: case CURL_SSLVERSION_MAX_TLSv1_3: -#ifdef TLS13_SUPPORT +#ifdef HAS_TLS13_SUPPORT ver_max = MBEDTLS_SSL_VERSION_TLS1_3; break; #endif @@ -395,7 +403,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, if(!selected) return CURLE_OUT_OF_MEMORY; -#ifndef TLS13_SUPPORT +#ifndef HAS_TLS13_SUPPORT (void) ciphers13, (void) j; #else if(!ciphers13) { @@ -443,7 +451,7 @@ add_ciphers: selected[count++] = id; } -#ifdef TLS13_SUPPORT +#ifdef HAS_TLS13_SUPPORT if(ciphers == ciphers13 && ciphers12) { ciphers = ciphers12; goto add_ciphers; @@ -585,7 +593,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_NOT_BUILT_IN; } -#ifdef THREADING_SUPPORT +#ifdef HAS_THREADING_SUPPORT mbedtls_ctr_drbg_init(&backend->ctr_drbg); ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex, @@ -608,7 +616,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) -ret, errorbuf); return CURLE_FAILED_INIT; } -#endif /* THREADING_SUPPORT */ +#endif /* HAS_THREADING_SUPPORT */ /* Load the trusted CA */ mbedtls_x509_crt_init(&backend->cacert); @@ -802,7 +810,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } -#ifdef MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED +#if defined(HAS_SESSION_TICKETS) && MBEDTLS_VERSION_NUMBER >= 0x03060100 /* New in mbedTLS 3.6.1, need to enable, default is now disabled */ mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config, MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); @@ -841,7 +849,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_bio_cf_read, NULL /* rev_timeout() */); -#ifndef TLS13_SUPPORT +#ifndef HAS_TLS13_SUPPORT if(conn_config->cipher_list) { CURLcode result = mbed_set_selected_ciphers(data, backend, conn_config->cipher_list, @@ -875,29 +883,30 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Check if there is a cached ID we can/should use here! */ if(ssl_config->primary.cache_session) { - void *sdata = NULL; - size_t slen = 0; + struct Curl_ssl_session *sc_session = NULL; + CURLcode result; - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - &sdata, &slen, NULL) && slen) { + result = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key, + &sc_session); + if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) { mbedtls_ssl_session session; mbedtls_ssl_session_init(&session); - ret = mbedtls_ssl_session_load(&session, sdata, slen); + ret = mbedtls_ssl_session_load(&session, sc_session->sdata, + sc_session->sdata_len); if(ret) { - failf(data, "error loading cached session: -0x%x", -ret); + failf(data, "SSL session error loading: -0x%x", -ret); } else { ret = mbedtls_ssl_set_session(&backend->ssl, &session); if(ret) - failf(data, "error setting session: -0x%x", -ret); + failf(data, "SSL session error setting: -0x%x", -ret); else infof(data, "SSL reusing session ID"); } mbedtls_ssl_session_free(&session); } - Curl_ssl_sessionid_unlock(data); + Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session); } mbedtls_ssl_conf_ca_chain(&backend->config, @@ -1115,12 +1124,6 @@ pinnedpubkey_error: return CURLE_OK; } -static void mbedtls_session_free(void *session, size_t slen) -{ - (void)slen; - free(session); -} - static CURLcode mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1128,48 +1131,64 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + mbedtls_ssl_session session; + bool msession_alloced = FALSE; + struct Curl_ssl_session *sc_session = NULL; + unsigned char *sdata = NULL; + size_t slen = 0; + int ietf_tls_id; CURLcode result = CURLE_OK; + int ret; DEBUGASSERT(backend); - if(ssl_config->primary.cache_session) { - int ret; - mbedtls_ssl_session session; - unsigned char *sdata = NULL; - size_t slen = 0; + if(!ssl_config->primary.cache_session) + return CURLE_OK; - mbedtls_ssl_session_init(&session); - ret = mbedtls_ssl_get_session(&backend->ssl, &session); - if(ret) { - if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED) - mbedtls_ssl_session_free(&session); - failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; - } + mbedtls_ssl_session_init(&session); + ret = mbedtls_ssl_get_session(&backend->ssl, &session); + msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED); + if(ret) { + failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } - mbedtls_ssl_session_save(&session, NULL, 0, &slen); - if(!slen) { - failf(data, "failed to serialize session: length is 0"); - } - else { - sdata = malloc(slen); - if(sdata) { - ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen); - if(ret) { - failf(data, "failed to serialize session: -0x%x", -ret); - } - else { - Curl_ssl_sessionid_lock(data); - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, - sdata, slen, mbedtls_session_free); - Curl_ssl_sessionid_unlock(data); - if(!result) - sdata = NULL; - } - } - } - mbedtls_ssl_session_free(&session); - free(sdata); + mbedtls_ssl_session_save(&session, NULL, 0, &slen); + if(!slen) { + failf(data, "failed to serialize session: length is 0"); + goto out; + } + + sdata = malloc(slen); + if(!sdata) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen); + if(ret) { + failf(data, "failed to serialize session: -0x%x", -ret); + goto out; } + +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + ietf_tls_id = mbedtls_ssl_get_version_number(&backend->ssl); +#else + ietf_tls_id = CURL_IETF_PROTO_UNKNOWN; +#endif + result = Curl_ssl_session_create(sdata, slen, + ietf_tls_id, + connssl->negotiated.alpn, 0, 0, + &sc_session); + sdata = NULL; /* call took ownership */ + if(!result) + result = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key, + sc_session); + +out: + if(msession_alloced) + mbedtls_ssl_session_free(&session); + free(sdata); return result; } @@ -1184,27 +1203,41 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, (void)data; DEBUGASSERT(backend); + /* mbedtls is picky when a mbedtls_ssl_write) was previously blocked. + * It requires to be called with the same amount of bytes again, or it + * will lose bytes, e.g. reporting all was sent but they were not. + * Remember the blocked length and use that when set. */ + if(backend->send_blocked) { + DEBUGASSERT(backend->send_blocked_len <= len); + CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> previously blocked " + "on %zu bytes", len, backend->send_blocked_len); + len = backend->send_blocked_len; + } + ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); if(ret < 0) { CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", len, -ret); *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE) -#ifdef TLS13_SUPPORT +#ifdef HAS_TLS13_SUPPORT || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) #endif ) ? CURLE_AGAIN : CURLE_SEND_ERROR; ret = -1; + if((*curlcode == CURLE_AGAIN) && !backend->send_blocked) { + backend->send_blocked = TRUE; + backend->send_blocked_len = len; + } + } + else { + CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d", len, ret); + backend->send_blocked = FALSE; } return ret; } -static void mbedtls_close_all(struct Curl_easy *data) -{ - (void)data; -} - static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool send_shutdown, bool *done) @@ -1260,7 +1293,7 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, * WANT_READ, but has not encountered an EAGAIN. */ if(ret == MBEDTLS_ERR_SSL_WANT_READ) ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf)); -#ifdef TLS13_SUPPORT +#ifdef HAS_TLS13_SUPPORT if(ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) continue; #endif @@ -1314,9 +1347,9 @@ static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_ssl_config_free(&backend->config); mbedtls_ssl_free(&backend->ssl); mbedtls_ctr_drbg_free(&backend->ctr_drbg); -#ifndef THREADING_SUPPORT +#ifndef HAS_THREADING_SUPPORT mbedtls_entropy_free(&backend->entropy); -#endif /* THREADING_SUPPORT */ +#endif /* HAS_THREADING_SUPPORT */ backend->initialized = FALSE; } } @@ -1558,29 +1591,29 @@ static int mbedtls_init(void) { if(!Curl_mbedtlsthreadlock_thread_setup()) return 0; -#ifdef THREADING_SUPPORT +#ifdef HAS_THREADING_SUPPORT entropy_init_mutex(&ts_entropy); #endif -#ifdef TLS13_SUPPORT +#ifdef HAS_PSA_SUPPORT { int ret; -#ifdef THREADING_SUPPORT +#ifdef HAS_THREADING_SUPPORT Curl_mbedtlsthreadlock_lock_function(0); #endif ret = psa_crypto_init(); -#ifdef THREADING_SUPPORT +#ifdef HAS_THREADING_SUPPORT Curl_mbedtlsthreadlock_unlock_function(0); #endif if(ret != PSA_SUCCESS) return 0; } -#endif /* TLS13_SUPPORT */ +#endif /* HAS_PSA_SUPPORT */ return 1; } static void mbedtls_cleanup(void) { -#ifdef THREADING_SUPPORT +#ifdef HAS_THREADING_SUPPORT entropy_cleanup_mutex(&ts_entropy); #endif (void)Curl_mbedtlsthreadlock_thread_cleanup(); @@ -1637,7 +1670,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { SSLSUPP_CERTINFO | SSLSUPP_PINNEDPUBKEY | SSLSUPP_SSL_CTX | -#ifdef TLS13_SUPPORT +#ifdef HAS_TLS13_SUPPORT SSLSUPP_TLS13_CIPHERSUITES | #endif SSLSUPP_HTTPS_PROXY | @@ -1648,24 +1681,21 @@ const struct Curl_ssl Curl_ssl_mbedtls = { mbedtls_init, /* init */ mbedtls_cleanup, /* cleanup */ mbedtls_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ mbedtls_shutdown, /* shutdown */ mbedtls_data_pending, /* data_pending */ mbedtls_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ + NULL, /* cert_status_request */ mbedtls_connect, /* connect */ mbedtls_connect_nonblocking, /* connect_nonblocking */ Curl_ssl_adjust_pollset, /* adjust_pollset */ mbedtls_get_internals, /* get_internals */ mbedtls_close, /* close_one */ - mbedtls_close_all, /* close_all */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ + NULL, /* close_all */ + NULL, /* set_engine */ + NULL, /* set_engine_default */ + NULL, /* engines_list */ + NULL, /* false_start */ mbedtls_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ mbed_recv, /* recv decrypted data */ mbed_send, /* send data to encrypt */ NULL, /* get_channel_binding */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 1b0e8dd..f34ddaf 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -56,6 +56,7 @@ #include "select.h" #include "vtls.h" #include "vtls_int.h" +#include "vtls_scache.h" #include "vauth/vauth.h" #include "keylog.h" #include "strcase.h" @@ -82,16 +83,15 @@ #include #include -#ifdef USE_ECH +#if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST) +#define USE_ECH_OPENSSL +#endif + +#ifdef USE_ECH_OPENSSL # if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) # include # endif -# include "curl_base64.h" -# define ECH_ENABLED(__data__) \ - (__data__->set.tls_ech && \ - !(__data__->set.tls_ech & CURLECH_DISABLE)\ - ) -#endif /* USE_ECH */ +#endif /* USE_ECH_OPENSSL */ #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP) #include @@ -103,6 +103,13 @@ #include #endif +#if OPENSSL_VERSION_NUMBER >= 0x03000000fL && !defined(OPENSSL_NO_UI_CONSOLE) +#include +#include +/* this is used in the following conditions to make them easier to read */ +#define OPENSSL_HAS_PROVIDERS +#endif + #include "warnless.h" /* The last #include files should be: */ @@ -124,7 +131,7 @@ #error "OPENSSL_VERSION_NUMBER not defined" #endif -#ifdef USE_OPENSSL_ENGINE +#if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS) #include #endif @@ -172,10 +179,6 @@ #define HAVE_X509_GET0_SIGNATURE 1 #endif -#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */ -#define HAVE_SSL_GET_SHUTDOWN 1 -#endif - #if OPENSSL_VERSION_NUMBER >= 0x10002003L && \ OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \ !defined(OPENSSL_NO_COMP) @@ -200,18 +203,6 @@ #define FREE_PKEY_PARAM_BIGNUM(name) #endif -/* - * Whether SSL_CTX_set_keylog_callback is available. - * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 - * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) - * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing. - */ -#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ - !defined(LIBRESSL_VERSION_NUMBER)) || \ - defined(OPENSSL_IS_BORINGSSL) -#define HAVE_KEYLOG_CALLBACK -#endif - /* Whether SSL_CTX_set_ciphersuites is available. * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) * BoringSSL: no @@ -739,7 +730,7 @@ static long ossl_bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr) #ifdef BIO_CTRL_EOF case BIO_CTRL_EOF: /* EOF has been reached on input? */ - return (!cf->next || !cf->next->connected); + return !cf->next || !cf->next->connected; #endif default: ret = 0; @@ -960,8 +951,6 @@ static const char *SSL_ERROR_to_str(int err) } } -static size_t ossl_version(char *buffer, size_t size); - /* Return error string for last OpenSSL error */ static char *ossl_strerror(unsigned long error, char *buf, size_t size) @@ -970,7 +959,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size) DEBUGASSERT(size); *buf = '\0'; - len = ossl_version(buf, size); + len = Curl_ossl_version(buf, size); DEBUGASSERT(len < (size - 2)); if(len < (size - 2)) { buf += len; @@ -1015,7 +1004,7 @@ static int passwd_callback(char *buf, int num, int encrypting, */ static bool rand_enough(void) { - return (0 != RAND_status()); + return 0 != RAND_status(); } static CURLcode ossl_seed(struct Curl_easy *data) @@ -1071,8 +1060,8 @@ static CURLcode ossl_seed(struct Curl_easy *data) } infof(data, "libcurl is now using a weak random seed"); - return (rand_enough() ? CURLE_OK : - CURLE_SSL_CONNECT_ERROR /* confusing error code */); + return rand_enough() ? CURLE_OK : + CURLE_SSL_CONNECT_ERROR; /* confusing error code */ #endif } @@ -1082,6 +1071,9 @@ static CURLcode ossl_seed(struct Curl_easy *data) #ifndef SSL_FILETYPE_PKCS12 #define SSL_FILETYPE_PKCS12 43 #endif +#ifndef SSL_FILETYPE_PROVIDER +#define SSL_FILETYPE_PROVIDER 44 +#endif static int ossl_do_file_type(const char *type) { if(!type || !type[0]) @@ -1090,6 +1082,8 @@ static int ossl_do_file_type(const char *type) return SSL_FILETYPE_PEM; if(strcasecompare(type, "DER")) return SSL_FILETYPE_ASN1; + if(strcasecompare(type, "PROV")) + return SSL_FILETYPE_PROVIDER; if(strcasecompare(type, "ENG")) return SSL_FILETYPE_ENGINE; if(strcasecompare(type, "P12")) @@ -1097,7 +1091,7 @@ static int ossl_do_file_type(const char *type) return -1; } -#ifdef USE_OPENSSL_ENGINE +#if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS) /* * Supply default password to the engine user interface conversation. * The password is passed by OpenSSL engine from ENGINE_load_private_key() @@ -1145,12 +1139,16 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis) */ static bool is_pkcs11_uri(const char *string) { - return (string && strncasecompare(string, "pkcs11:", 7)); + return string && strncasecompare(string, "pkcs11:", 7); } #endif static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine); +#if !defined(USE_OPENSSL_ENGINE) && defined(OPENSSL_HAS_PROVIDERS) +static CURLcode ossl_set_provider(struct Curl_easy *data, + const char *provider); +#endif static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob, int type, const char *key_passwd) @@ -1299,7 +1297,8 @@ int cert_stuff(struct Curl_easy *data, int file_type = ossl_do_file_type(cert_type); - if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) { + if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE) || + (file_type == SSL_FILETYPE_PROVIDER)) { SSL *ssl; X509 *x509; int cert_done = 0; @@ -1410,8 +1409,79 @@ int cert_stuff(struct Curl_easy *data, } } break; +#elif defined(OPENSSL_HAS_PROVIDERS) + /* fall through to compatible provider */ + case SSL_FILETYPE_PROVIDER: + { + /* Implicitly use pkcs11 provider if none was provided and the + * cert_file is a PKCS#11 URI */ + if(!data->state.provider) { + if(is_pkcs11_uri(cert_file)) { + if(ossl_set_provider(data, "pkcs11") != CURLE_OK) { + return 0; + } + } + } + + if(data->state.provider) { + /* Load the certificate from the provider */ + OSSL_STORE_CTX *store = NULL; + OSSL_STORE_INFO *info = NULL; + X509 *cert = NULL; + store = OSSL_STORE_open(cert_file, NULL, NULL, NULL, NULL); + if(!store) { + failf(data, "Failed to open OpenSSL store: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } + if(OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) != 1) { + failf(data, "Failed to set store preference. Ignoring the error: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + } + + for(info = OSSL_STORE_load(store); + info != NULL; + info = OSSL_STORE_load(store)) { + int ossl_type = OSSL_STORE_INFO_get_type(info); + + if(ossl_type == OSSL_STORE_INFO_CERT) { + cert = OSSL_STORE_INFO_get1_CERT(info); + } + else { + failf(data, "Ignoring object not matching our type: %d", + ossl_type); + OSSL_STORE_INFO_free(info); + continue; + } + OSSL_STORE_INFO_free(info); + break; + } + OSSL_STORE_close(store); + if(!cert) { + failf(data, "No cert found in the openssl store: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + goto fail; + } + + if(SSL_CTX_use_certificate(ctx, cert) != 1) { + failf(data, "unable to set client certificate [%s]", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } + X509_free(cert); /* we do not need the handle any more... */ + } + else { + failf(data, "crypto provider not set, cannot load certificate"); + return 0; + } + } + break; #else - failf(data, "file type ENG for certificate not implemented"); + failf(data, "file type ENG nor PROV for certificate not implemented"); return 0; #endif @@ -1603,10 +1673,96 @@ fail: } } break; +#elif defined(OPENSSL_HAS_PROVIDERS) + /* fall through to compatible provider */ + case SSL_FILETYPE_PROVIDER: + { + /* Implicitly use pkcs11 provider if none was provided and the + * cert_file is a PKCS#11 URI */ + if(!data->state.provider) { + if(is_pkcs11_uri(cert_file)) { + if(ossl_set_provider(data, "pkcs11") != CURLE_OK) { + return 0; + } + } + } + + if(data->state.provider) { + /* Load the private key from the provider */ + EVP_PKEY *priv_key = NULL; + OSSL_STORE_CTX *store = NULL; + OSSL_STORE_INFO *info = NULL; + UI_METHOD *ui_method = + UI_create_method((char *)"curl user interface"); + if(!ui_method) { + failf(data, "unable do create " OSSL_PACKAGE + " user-interface method"); + return 0; + } + UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); + UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL())); + UI_method_set_reader(ui_method, ssl_ui_reader); + UI_method_set_writer(ui_method, ssl_ui_writer); + + store = OSSL_STORE_open(key_file, ui_method, NULL, NULL, NULL); + if(!store) { + failf(data, "Failed to open OpenSSL store: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } + if(OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) { + failf(data, "Failed to set store preference. Ignoring the error: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + } + + for(info = OSSL_STORE_load(store); + info != NULL; + info = OSSL_STORE_load(store)) { + int ossl_type = OSSL_STORE_INFO_get_type(info); + + if(ossl_type == OSSL_STORE_INFO_PKEY) { + priv_key = OSSL_STORE_INFO_get1_PKEY(info); + } + else { + failf(data, "Ignoring object not matching our type: %d", + ossl_type); + OSSL_STORE_INFO_free(info); + continue; + } + OSSL_STORE_INFO_free(info); + break; + } + OSSL_STORE_close(store); + UI_destroy_method(ui_method); + if(!priv_key) { + failf(data, "No private key found in the openssl store: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + goto fail; + } + + if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) { + failf(data, "unable to set private key [%s]", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + EVP_PKEY_free(priv_key); + return 0; + } + EVP_PKEY_free(priv_key); /* we do not need the handle any more... */ + } + else { + failf(data, "crypto provider not set, cannot load private key"); + return 0; + } + } + break; #else - failf(data, "file type ENG for private key not supported"); + failf(data, "file type ENG nor PROV for private key not implemented"); return 0; #endif + case SSL_FILETYPE_PKCS12: if(!cert_done) { failf(data, "file type P12 for private key not supported"); @@ -1874,6 +2030,40 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) return list; } +#if !defined(USE_OPENSSL_ENGINE) && defined(OPENSSL_HAS_PROVIDERS) +/* Selects an OpenSSL crypto provider + */ +static CURLcode ossl_set_provider(struct Curl_easy *data, const char *provider) +{ + OSSL_PROVIDER *pkcs11_provider = NULL; + char error_buffer[256]; + + if(OSSL_PROVIDER_available(NULL, provider)) { + /* already loaded through the configuration - no action needed */ + data->state.provider = TRUE; + return CURLE_OK; + } + if(data->state.provider_failed) { + return CURLE_SSL_ENGINE_NOTFOUND; + } + + pkcs11_provider = OSSL_PROVIDER_try_load(NULL, provider, 1); + if(!pkcs11_provider) { + failf(data, "Failed to initialize provider: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + /* Do not attempt to load it again */ + data->state.provider_failed = TRUE; + /* FIXME not the right error but much less fuss than creating a new + * public one */ + return CURLE_SSL_ENGINE_NOTFOUND; + } + data->state.provider = TRUE; + return CURLE_OK; +} +#endif + + static CURLcode ossl_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool send_shutdown, bool *done) @@ -2013,13 +2203,6 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) } } -static void ossl_session_free(void *sessionid, size_t idsize) -{ - /* free the ID */ - (void)idsize; - free(sessionid); -} - /* * This function is called when the 'data' struct is going away. Close * down everything and free all resources! @@ -2873,20 +3056,23 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, struct Curl_easy *data, - const struct ssl_peer *peer, - SSL_SESSION *session) + const char *ssl_peer_key, + SSL_SESSION *session, + int ietf_tls_id, + const char *alpn) { const struct ssl_config_data *config; + unsigned char *der_session_buf = NULL; CURLcode result = CURLE_OK; - size_t der_session_size; - unsigned char *der_session_buf; - unsigned char *der_session_ptr; if(!cf || !data) goto out; config = Curl_ssl_cf_get_config(cf, data); if(config->primary.cache_session) { + struct Curl_ssl_session *sc_session = NULL; + size_t der_session_size; + unsigned char *der_session_ptr; der_session_size = i2d_SSL_SESSION(session, NULL); if(der_session_size == 0) { @@ -2903,17 +3089,23 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, 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, NULL, der_session_buf, - der_session_size, ossl_session_free); - Curl_ssl_sessionid_unlock(data); + result = Curl_ssl_session_create(der_session_buf, der_session_size, + ietf_tls_id, alpn, + (curl_off_t)time(NULL) + + SSL_SESSION_get_timeout(session), 0, + &sc_session); + der_session_buf = NULL; /* took ownership of sdata */ + if(!result) { + result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session); + /* took ownership of `sc_session` */ + } } out: + free(der_session_buf); return result; } @@ -2922,14 +3114,13 @@ out: */ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { - struct Curl_cfilter *cf; - struct Curl_easy *data; - struct ssl_connect_data *connssl; - - cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); - connssl = cf ? cf->ctx : NULL; - data = connssl ? CF_DATA_CURRENT(cf) : NULL; - Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid); + struct Curl_cfilter *cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); + if(cf) { + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct ssl_connect_data *connssl = cf->ctx; + Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid, + SSL_version(ssl), connssl->negotiated.alpn); + } return 0; } @@ -3133,9 +3324,9 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, } #endif -static CURLcode populate_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - X509_STORE *store) +static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); @@ -3151,7 +3342,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, bool imported_native_ca = FALSE; bool imported_ca_info_blob = FALSE; - CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d", + CURL_TRC_CF(data, cf, "ossl_populate_x509_store, path=%s, blob=%d", ssl_cafile ? ssl_cafile : "none", !!ca_info_blob); if(!store) return CURLE_OUT_OF_MEMORY; @@ -3322,8 +3513,8 @@ static void oss_x509_share_free(void *key, size_t key_len, void *p) } static bool -cached_x509_store_expired(const struct Curl_easy *data, - const struct ossl_x509_share *mb) +ossl_cached_x509_store_expired(const struct Curl_easy *data, + const struct ossl_x509_share *mb) { const struct ssl_general_config *cfg = &data->set.general_ssl; if(cfg->ca_cache_timeout < 0) @@ -3338,8 +3529,8 @@ cached_x509_store_expired(const struct Curl_easy *data, } static bool -cached_x509_store_different(struct Curl_cfilter *cf, - const struct ossl_x509_share *mb) +ossl_cached_x509_store_different(struct Curl_cfilter *cf, + const struct ossl_x509_share *mb) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); if(!mb->CAfile || !conn_config->CAfile) @@ -3348,8 +3539,8 @@ cached_x509_store_different(struct Curl_cfilter *cf, return strcmp(mb->CAfile, conn_config->CAfile); } -static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data) +static X509_STORE *ossl_get_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) { struct Curl_multi *multi = data->multi; struct ossl_x509_share *share; @@ -3360,17 +3551,17 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, (void *)MPROTO_OSSL_X509_KEY, sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL; if(share && share->store && - !cached_x509_store_expired(data, share) && - !cached_x509_store_different(cf, share)) { + !ossl_cached_x509_store_expired(data, share) && + !ossl_cached_x509_store_different(cf, share)) { store = share->store; } return store; } -static void set_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data, - X509_STORE *store) +static void ossl_set_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; @@ -3438,16 +3629,16 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, !ssl_config->primary.CRLfile && !ssl_config->native_ca_store; - cached_store = get_cached_x509_store(cf, data); + cached_store = ossl_get_cached_x509_store(cf, data); if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) { SSL_CTX_set_cert_store(ssl_ctx, cached_store); } else { X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); - result = populate_x509_store(cf, data, store); + result = ossl_populate_x509_store(cf, data, store); if(result == CURLE_OK && cache_criteria_met) { - set_cached_x509_store(cf, data, store); + ossl_set_cached_x509_store(cf, data, store); } } @@ -3460,7 +3651,7 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, { X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); - return populate_x509_store(cf, data, store); + return ossl_populate_x509_store(cf, data, store); } #endif /* HAVE_SSL_X509_STORE_SHARE */ @@ -3468,7 +3659,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - int transport, /* TCP or QUIC */ const unsigned char *alpn, size_t alpn_len, Curl_ossl_ctx_setup_cb *cb_setup, void *cb_user_data, @@ -3479,9 +3669,6 @@ 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; - 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; @@ -3498,7 +3685,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ssl_config->certverifyresult = !X509_V_OK; - switch(transport) { + switch(peer->transport) { case TRNSPRT_TCP: /* check to see if we have been told to use an explicit SSL/TLS version */ switch(ssl_version_min) { @@ -3542,7 +3729,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #endif break; default: - failf(data, "unsupported transport %d in SSL init", transport); + failf(data, "unsupported transport %d in SSL init", peer->transport); return CURLE_SSL_CONNECT_ERROR; } @@ -3832,7 +4019,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } } -#ifdef USE_ECH +#ifdef USE_ECH_OPENSSL if(ECH_ENABLED(data)) { unsigned char *ech_config = NULL; size_t ech_config_len = 0; @@ -3865,7 +4052,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } if(SSL_set1_ech_config_list(octx->ssl, ech_config, ech_config_len) != 1) { - infof(data, "ECH: SSL_ECH_set1_echconfig failed"); + infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); if(data->set.tls_ech & CURLECH_HARD) { free(ech_config); return CURLE_SSL_CONNECT_ERROR; @@ -3880,8 +4067,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return CURLE_SSL_CONNECT_ERROR; } ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]); - if(SSL_ech_set1_echconfig(octx->ssl, ech_config, ech_config_len) != 1) { - infof(data, "ECH: SSL_ECH_set1_echconfig failed"); + if(SSL_set1_ech_config_list(octx->ssl, ech_config, + ech_config_len) != 1) { + infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } @@ -3909,19 +4097,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, size_t elen = rinfo->echconfiglist_len; infof(data, "ECH: ECHConfig from DoH HTTPS RR"); -# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) - if(SSL_ech_set1_echconfig(octx->ssl, ecl, elen) != 1) { - infof(data, "ECH: SSL_ECH_set1_echconfig failed"); - if(data->set.tls_ech & CURLECH_HARD) - return CURLE_SSL_CONNECT_ERROR; - } -# else if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { - infof(data, "ECH: SSL_set1_ech_config_list failed (BoringSSL)"); + infof(data, "ECH: SSL_set1_ech_config_list failed"); if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } -# endif else { trying_ech_now = 1; infof(data, "ECH: imported ECHConfigList of length %zu", elen); @@ -3944,7 +4124,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, if(trying_ech_now && outername) { infof(data, "ECH: inner: '%s', outer: '%s'", peer->hostname ? peer->hostname : "NULL", outername); - result = SSL_ech_set_server_names(octx->ssl, + result = SSL_ech_set1_server_names(octx->ssl, peer->hostname, outername, 0 /* do send outer */); if(result != 1) { @@ -3959,38 +4139,42 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return CURLE_SSL_CONNECT_ERROR; } } -#endif /* USE_ECH */ +#endif /* USE_ECH_OPENSSL */ #endif octx->reused_session = FALSE; if(ssl_config->primary.cache_session) { - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid, - &der_sessionid_size, NULL)) { - /* we got a session id, use it! */ + struct Curl_ssl_session *sc_session = NULL; + + result = Curl_ssl_scache_take(cf, data, peer->scache_key, &sc_session); + if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) { + const unsigned char *der_sessionid = sc_session->sdata; + size_t der_sessionid_size = sc_session->sdata_len; + SSL_SESSION *ssl_session = NULL; + + /* If OpenSSL does not accept the session from the cache, this + * is not an error. We just continue without it. */ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid, - (long)der_sessionid_size); + (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", + infof(data, "SSL: SSL_set_session not accepted, " + "continuing without: %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; + } + else { + infof(data, "SSL reusing session"); + octx->reused_session = TRUE; } 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; + infof(data, "SSL session not accepted by OpenSSL, continuing without"); } } - Curl_ssl_sessionid_unlock(data); + Curl_ssl_scache_return(cf, data, peer->scache_key, sc_session); } return CURLE_OK; @@ -4018,7 +4202,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif - result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP, + result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, proto.data, proto.len, NULL, NULL, ossl_new_session_cb, cf); if(result) @@ -4055,7 +4239,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK; } -#ifdef USE_ECH +#ifdef USE_ECH_OPENSSL /* If we have retry configs, then trace those out */ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl, int reason) @@ -4079,35 +4263,34 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl, if(!ECH_ENABLED(data)) return; # if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) - rv = SSL_ech_get_retry_config(ssl, &rcs, &rcl); + rv = SSL_ech_get1_retry_config(ssl, &rcs, &rcl); # else SSL_get0_ech_retry_configs(ssl, &rcs, &rcl); rv = (int)rcl; # endif if(rv && rcs) { -# define HEXSTR_MAX 800 char *b64str = NULL; size_t blen = 0; - result = Curl_base64_encode((const char *)rcs, rcl, - &b64str, &blen); - if(!result && b64str) + result = Curl_base64_encode((const char *)rcs, rcl, &b64str, &blen); + if(!result && b64str) { infof(data, "ECH: retry_configs %s", b64str); - free(b64str); -# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) - rv = SSL_ech_get_status(ssl, &inner, &outer); - infof(data, "ECH: retry_configs for %s from %s, %d %d", - inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); -# else - rv = SSL_ech_accepted(ssl); - servername_type = SSL_get_servername_type(ssl); - inner = SSL_get_servername(ssl, servername_type); - SSL_get0_ech_name_override(ssl, &outer, &out_name_len); - /* TODO: get the inner from BoringSSL */ - infof(data, "ECH: retry_configs for %s from %s, %d %d", - inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); -# endif + free(b64str); +#if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) + rv = SSL_ech_get1_status(ssl, &inner, &outer); + infof(data, "ECH: retry_configs for %s from %s, %d %d", + inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); +#else + rv = SSL_ech_accepted(ssl); + servername_type = SSL_get_servername_type(ssl); + inner = SSL_get_servername(ssl, servername_type); + SSL_get0_ech_name_override(ssl, &outer, &out_name_len); + /* TODO: get the inner from BoringSSL */ + infof(data, "ECH: retry_configs for %s from %s, %d %d", + inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); +#endif + } } else infof(data, "ECH: no retry_configs (rv = %d)", rv); @@ -4230,7 +4413,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, ossl_strerror(errdetail, error_buffer, sizeof(error_buffer))); } #endif -#ifdef USE_ECH +#ifdef USE_ECH_OPENSSL else if((lib == ERR_LIB_SSL) && # if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) (reason == SSL_R_ECH_REQUIRED)) { @@ -4296,14 +4479,14 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, negotiated_group_name ? negotiated_group_name : "[blank]", OBJ_nid2sn(psigtype_nid)); -#ifdef USE_ECH +#ifdef USE_ECH_OPENSSL # if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) if(ECH_ENABLED(data)) { char *inner = NULL, *outer = NULL; const char *status = NULL; int rv; - rv = SSL_ech_get_status(octx->ssl, &inner, &outer); + rv = SSL_ech_get1_status(octx->ssl, &inner, &outer); switch(rv) { case SSL_ECH_STATUS_SUCCESS: status = "succeeded"; @@ -4356,7 +4539,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, infof(data, "ECH: result: status is not attempted"); } # endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ -#endif /* USE_ECH */ +#endif /* USE_ECH_OPENSSL */ #ifdef HAS_ALPN /* Sets data and len to negotiated protocol, len is 0 if no protocol was @@ -4693,21 +4876,6 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, /* do not do this after Session ID reuse */ result = verifystatus(cf, data, octx); if(result) { - /* when verifystatus failed, remove the session id from the cache again - if present */ - if(!Curl_ssl_cf_is_proxy(cf)) { - void *old_ssl_sessionid = NULL; - bool incache; - Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, peer, - &old_ssl_sessionid, NULL, NULL)); - if(incache) { - infof(data, "Remove session ID again from cache"); - Curl_ssl_delsessionid(data, old_ssl_sessionid); - } - Curl_ssl_sessionid_unlock(data); - } - X509_free(octx->server_cert); octx->server_cert = NULL; return result; @@ -4757,6 +4925,9 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer); if(!result) connssl->connecting_state = ssl_connect_done; + else + /* on error, remove sessions we might have in the pool */ + Curl_ssl_scache_remove_all(cf, data, connssl->peer.scache_key); return result; } @@ -5172,7 +5343,7 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, #endif } -static size_t ossl_version(char *buffer, size_t size) +size_t Curl_ossl_version(char *buffer, size_t size) { #ifdef LIBRESSL_VERSION_NUMBER #ifdef HAVE_OPENSSL_VERSION @@ -5267,7 +5438,7 @@ static CURLcode ossl_random(struct Curl_easy *data, } /* RAND_bytes() returns 1 on success, 0 otherwise. */ rc = RAND_bytes(entropy, (ossl_valsize_t)curlx_uztosi(length)); - return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT); + return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT; } #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) @@ -5325,7 +5496,7 @@ const struct Curl_ssl Curl_ssl_openssl = { #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES SSLSUPP_TLS13_CIPHERSUITES | #endif -#ifdef USE_ECH +#ifdef USE_ECH_OPENSSL SSLSUPP_ECH | #endif SSLSUPP_CA_CACHE | @@ -5336,8 +5507,7 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_init, /* init */ ossl_cleanup, /* cleanup */ - ossl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ + Curl_ossl_version, /* version */ ossl_shutdown, /* shutdown */ ossl_data_pending, /* data_pending */ ossl_random, /* random */ @@ -5351,14 +5521,12 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_set_engine, /* set_engine */ ossl_set_engine_default, /* set_engine_default */ ossl_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ + NULL, /* false_start */ #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) ossl_sha256sum, /* sha256sum */ #else NULL, /* sha256sum */ #endif - NULL, /* use of data in this connection */ - 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 */ diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 7aba947..b2940f4 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -31,12 +31,27 @@ * This header should only be needed to get included by vtls.c, openssl.c * and ngtcp2.c */ +#include #include #include #include "urldata.h" -/* Struct to hold a Curl OpenSSL instance */ +/* + * Whether SSL_CTX_set_keylog_callback is available. + * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 + * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) + * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing. + */ +#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ + !defined(LIBRESSL_VERSION_NUMBER)) || \ + defined(OPENSSL_IS_BORINGSSL) +#define HAVE_KEYLOG_CALLBACK +#endif + +struct ssl_peer; + +/* Struct to hold a curl OpenSSL instance */ struct ossl_ctx { /* these ones requires specific SSL-types */ SSL_CTX* ssl_ctx; @@ -53,6 +68,8 @@ struct ossl_ctx { BIT(reused_session); /* session-ID was reused for this */ }; +size_t Curl_ossl_version(char *buffer, size_t size); + typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf, struct Curl_easy *data, void *user_data); @@ -63,7 +80,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - int transport, /* TCP or QUIC */ const unsigned char *alpn, size_t alpn_len, Curl_ossl_ctx_setup_cb *cb_setup, void *cb_user_data, @@ -94,8 +110,10 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf, */ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, struct Curl_easy *data, - const struct ssl_peer *peer, - SSL_SESSION *ssl_sessionid); + const char *ssl_peer_key, + SSL_SESSION *ssl_sessionid, + int ietf_tls_id, + const char *alpn); /* * Get the server cert, verify it and show it, etc., only call failf() if diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 5d14348..948d0e9 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -1076,27 +1076,24 @@ const struct Curl_ssl Curl_ssl_rustls = { SSLSUPP_TLS13_CIPHERSUITES, sizeof(struct rustls_ssl_backend_data), - Curl_none_init, /* init */ - Curl_none_cleanup, /* cleanup */ + NULL, /* init */ + NULL, /* cleanup */ cr_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ cr_shutdown, /* shutdown */ cr_data_pending, /* data_pending */ cr_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ + NULL, /* cert_status_request */ cr_connect_blocking, /* connect */ cr_connect_nonblocking, /* connect_nonblocking */ Curl_ssl_adjust_pollset, /* adjust_pollset */ cr_get_internals, /* get_internals */ cr_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ + NULL, /* close_all */ + NULL, /* set_engine */ + NULL, /* set_engine_default */ + NULL, /* engines_list */ + NULL, /* false_start */ NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ cr_recv, /* recv decrypted data */ cr_send, /* send data to encrypt */ NULL, /* get_channel_binding */ diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index f4bbe4e..8441b46 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -41,6 +41,7 @@ #include "schannel_int.h" #include "vtls.h" #include "vtls_int.h" +#include "vtls_scache.h" #include "strcase.h" #include "sendf.h" #include "connect.h" /* for the connect timeout */ @@ -148,7 +149,7 @@ */ #ifndef CALG_SHA_256 -# define CALG_SHA_256 0x0000800c +#define CALG_SHA_256 0x0000800c #endif #ifndef PKCS12_NO_PERSIST_KEY @@ -954,9 +955,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* check for an existing reusable credential handle */ if(ssl_config->primary.cache_session) { - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - (void **)&old_cred, NULL, NULL)) { + Curl_ssl_scache_lock(data); + if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key, + (void **)&old_cred)) { backend->cred = old_cred; DEBUGF(infof(data, "schannel: reusing existing credential handle")); @@ -966,7 +967,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) "schannel: incremented credential handle refcount = %d", backend->cred->refcount)); } - Curl_ssl_sessionid_unlock(data); + Curl_ssl_scache_unlock(data); } if(!backend->cred) { @@ -1501,12 +1502,11 @@ add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order, return args->result == CURLE_OK; } -static void schannel_session_free(void *sessionid, size_t idsize) +static void schannel_session_free(void *sessionid) { /* this is expected to be called under sessionid lock */ struct Curl_schannel_cred *cred = sessionid; - (void)idsize; if(cred) { cred->refcount--; if(cred->refcount == 0) { @@ -1599,14 +1599,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) /* save the current session data for possible reuse */ if(ssl_config->primary.cache_session) { - Curl_ssl_sessionid_lock(data); + Curl_ssl_scache_lock(data); /* Up ref count since call takes ownership */ backend->cred->refcount++; - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, - backend->cred, - sizeof(struct Curl_schannel_cred), - schannel_session_free); - Curl_ssl_sessionid_unlock(data); + result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key, + backend->cred, schannel_session_free); + Curl_ssl_scache_unlock(data); if(result) return result; } @@ -2275,11 +2273,11 @@ static bool schannel_data_pending(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(backend->ctxt) /* SSL/TLS is in use */ - return (backend->decdata_offset > 0 || - (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) || - backend->recv_connection_closed || - backend->recv_sspi_close_notify || - backend->recv_unrecoverable_err); + return backend->decdata_offset > 0 || + (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) || + backend->recv_connection_closed || + backend->recv_sspi_close_notify || + backend->recv_unrecoverable_err; else return FALSE; } @@ -2445,9 +2443,9 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) /* free SSPI Schannel API credential handle */ if(backend->cred) { - Curl_ssl_sessionid_lock(data); - schannel_session_free(backend->cred, 0); - Curl_ssl_sessionid_unlock(data); + Curl_ssl_scache_lock(data); + schannel_session_free(backend->cred); + Curl_ssl_scache_unlock(data); backend->cred = NULL; } @@ -2469,7 +2467,7 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) static int schannel_init(void) { - return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0); + return Curl_sspi_global_init() == CURLE_OK ? 1 : 0; } static void schannel_cleanup(void) @@ -2798,24 +2796,21 @@ const struct Curl_ssl Curl_ssl_schannel = { schannel_init, /* init */ schannel_cleanup, /* cleanup */ schannel_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ schannel_shutdown, /* shutdown */ schannel_data_pending, /* data_pending */ schannel_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ + NULL, /* cert_status_request */ schannel_connect, /* connect */ schannel_connect_nonblocking, /* connect_nonblocking */ Curl_ssl_adjust_pollset, /* adjust_pollset */ schannel_get_internals, /* get_internals */ schannel_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ + NULL, /* close_all */ + NULL, /* set_engine */ + NULL, /* set_engine_default */ + NULL, /* engines_list */ + NULL, /* false_start */ schannel_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ schannel_recv, /* recv decrypted data */ schannel_send, /* send data to encrypt */ NULL, /* get_channel_binding */ diff --git a/lib/vtls/schannel.h b/lib/vtls/schannel.h index b26334b..69f1bad 100644 --- a/lib/vtls/schannel.h +++ b/lib/vtls/schannel.h @@ -30,7 +30,7 @@ #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable: 4201) +#pragma warning(disable:4201) #endif #include #ifdef _MSC_VER diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c index c6a1c73..a0f6dcc 100644 --- a/lib/vtls/sectransp.c +++ b/lib/vtls/sectransp.c @@ -38,6 +38,7 @@ #include "multiif.h" #include "strcase.h" #include "x509asn1.h" +#include "vtls_scache.h" #include "strerror.h" #include "cipher_suite.h" @@ -51,6 +52,11 @@ #pragma GCC diagnostic ignored "-Waddress" #endif +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + #include #include @@ -1015,7 +1021,7 @@ failed: return ret; } -static void sectransp_session_free(void *sessionid, size_t idsize) +static void sectransp_session_free(void *sessionid) { /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a cached session ID inside the Security framework. There is a private @@ -1023,7 +1029,6 @@ static void sectransp_session_free(void *sessionid, size_t idsize) got your application rejected from the App Store due to the use of a private API, so the best we can do is free up our own char array that we created way back in sectransp_connect_step1... */ - (void)idsize; Curl_safefree(sessionid); } @@ -1332,19 +1337,19 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, char *ssl_sessionid; size_t ssl_sessionid_len; - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - (void **)&ssl_sessionid, &ssl_sessionid_len, - NULL)) { + Curl_ssl_scache_lock(data); + if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key, + (void **)&ssl_sessionid)) { /* we got a session id, use it! */ - err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); - Curl_ssl_sessionid_unlock(data); + err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, + strlen(ssl_sessionid)); + Curl_ssl_scache_unlock(data); if(err != noErr) { failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; } - /* Informational message */ - infof(data, "SSL reusing session ID"); + else + infof(data, "SSL reusing session ID"); } /* If there is not one, then let's make one up! This has to be done prior to starting the handshake. */ @@ -1358,15 +1363,17 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); if(err != noErr) { - Curl_ssl_sessionid_unlock(data); + Curl_ssl_scache_unlock(data); failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; } - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, - ssl_sessionid, ssl_sessionid_len, + /* This is all a bit weird, as we have not handshaked yet. + * I hope this backend will go away soon. */ + result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key, + (void *)ssl_sessionid, sectransp_session_free); - Curl_ssl_sessionid_unlock(data); + Curl_ssl_scache_unlock(data); if(result) return result; } @@ -1504,9 +1511,11 @@ static CURLcode append_cert_to_array(struct Curl_easy *data, case CURLE_OK: break; case CURLE_PEER_FAILED_VERIFICATION: + CFRelease(cacert); return CURLE_SSL_CACERT_BADFILE; case CURLE_OUT_OF_MEMORY: default: + CFRelease(cacert); return result; } free(certp); @@ -2424,7 +2433,7 @@ static CURLcode sectransp_shutdown(struct Curl_cfilter *cf, struct st_ssl_backend_data *backend = (struct st_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; - ssize_t nread; + ssize_t nread = 0; char buf[1024]; size_t i; @@ -2742,32 +2751,33 @@ const struct Curl_ssl Curl_ssl_sectransp = { sizeof(struct st_ssl_backend_data), - Curl_none_init, /* init */ - Curl_none_cleanup, /* cleanup */ + NULL, /* init */ + NULL, /* cleanup */ sectransp_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ sectransp_shutdown, /* shutdown */ sectransp_data_pending, /* data_pending */ sectransp_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ + NULL, /* cert_status_request */ sectransp_connect, /* connect */ sectransp_connect_nonblocking, /* connect_nonblocking */ Curl_ssl_adjust_pollset, /* adjust_pollset */ sectransp_get_internals, /* get_internals */ sectransp_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ + NULL, /* close_all */ + NULL, /* set_engine */ + NULL, /* set_engine_default */ + NULL, /* engines_list */ sectransp_false_start, /* false_start */ sectransp_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ sectransp_recv, /* recv decrypted data */ sectransp_send, /* send data to encrypt */ NULL, /* get_channel_binding */ }; +#if defined(__GNUC__) && defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + #ifdef __GNUC__ #pragma GCC diagnostic pop #endif diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 61b407a..c258b11 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -55,6 +55,7 @@ #include "vtls.h" /* generic SSL protos etc */ #include "vtls_int.h" +#include "vtls_scache.h" #include "openssl.h" /* OpenSSL versions */ #include "gtls.h" /* GnuTLS versions */ @@ -74,6 +75,7 @@ #include "multiif.h" #include "timeval.h" #include "curl_md5.h" +#include "curl_sha256.h" #include "warnless.h" #include "curl_base64.h" #include "curl_printf.h" @@ -88,11 +90,6 @@ #include "memdebug.h" -/* convenience macro to check if this handle is using a shared SSL session */ -#define SSLSESSION_SHARED(data) (data->share && \ - (data->share->specifier & \ - (1<var) { \ @@ -426,7 +423,9 @@ int Curl_ssl_init(void) return 1; init_ssl = TRUE; /* never again */ - return Curl_ssl->init(); + if(Curl_ssl->init) + return Curl_ssl->init(); + return 1; } static bool ssl_prefs_check(struct Curl_easy *data) @@ -463,9 +462,10 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, if(!ctx) return NULL; + ctx->ssl_impl = Curl_ssl; ctx->alpn = alpn; Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES); - ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data); + ctx->backend = calloc(1, ctx->ssl_impl->sizeof_ssl_backend_data); if(!ctx->backend) { free(ctx); return NULL; @@ -476,7 +476,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, static void cf_ctx_free(struct ssl_connect_data *ctx) { if(ctx) { - Curl_safefree(ctx->alpn_negotiated); + Curl_safefree(ctx->negotiated.alpn); Curl_bufq_free(&ctx->earlydata); free(ctx->backend); free(ctx); @@ -494,7 +494,7 @@ static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data) /* mark this is being ssl-enabled from here on. */ connssl->state = ssl_connection_negotiating; - result = Curl_ssl->connect_blocking(cf, data); + result = connssl->ssl_impl->connect_blocking(cf, data); if(!result) { DEBUGASSERT(connssl->state == ssl_connection_complete); @@ -507,275 +507,13 @@ static CURLcode ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { + struct ssl_connect_data *connssl = cf->ctx; + if(!ssl_prefs_check(data)) return CURLE_SSL_CONNECT_ERROR; /* mark this is being ssl requested from here on. */ - return Curl_ssl->connect_nonblocking(cf, data, done); -} - -/* - * Lock shared SSL session data - */ -void Curl_ssl_sessionid_lock(struct Curl_easy *data) -{ - if(SSLSESSION_SHARED(data)) - Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); -} - -/* - * Unlock shared SSL session data - */ -void Curl_ssl_sessionid_unlock(struct Curl_easy *data) -{ - if(SSLSESSION_SHARED(data)) - Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); -} - -/* - * Check if there is a session ID for the given connection in the cache, and if - * there is one suitable, it is provided. Returns TRUE when no entry matched. - */ -bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct ssl_peer *peer, - void **ssl_sessionid, - size_t *idsize, /* set 0 if unknown */ - char **palpn) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct Curl_ssl_session *check; - size_t i; - long *general_age; - bool no_match = TRUE; - - *ssl_sessionid = NULL; - if(palpn) - *palpn = NULL; - if(!ssl_config) - return TRUE; - - DEBUGASSERT(ssl_config->primary.cache_session); - - if(!ssl_config->primary.cache_session || !data->state.session) - /* session ID reuse is disabled or the session cache has not been - setup */ - return TRUE; - - /* Lock if shared */ - if(SSLSESSION_SHARED(data)) - general_age = &data->share->sessionage; - else - general_age = &data->state.sessionage; - - for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) { - check = &data->state.session[i]; - if(!check->sessionid) - /* not session ID means blank entry */ - continue; - if(strcasecompare(peer->hostname, check->name) && - ((!cf->conn->bits.conn_to_host && !check->conn_to_host) || - (cf->conn->bits.conn_to_host && check->conn_to_host && - strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) && - ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) || - (cf->conn->bits.conn_to_port && check->conn_to_port != -1 && - cf->conn->conn_to_port == check->conn_to_port)) && - (peer->port == check->remote_port) && - (peer->transport == check->transport) && - strcasecompare(cf->conn->handler->scheme, check->scheme) && - match_ssl_primary_config(data, conn_config, &check->ssl_config)) { - /* yes, we have a session ID! */ - (*general_age)++; /* increase general age */ - check->age = *general_age; /* set this as used in this age */ - *ssl_sessionid = check->sessionid; - if(idsize) - *idsize = check->idsize; - if(palpn) - *palpn = check->alpn; - no_match = FALSE; - break; - } - } - - 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; -} - -/* - * Kill a single session ID entry in the cache. - */ -void Curl_ssl_kill_session(struct Curl_ssl_session *session) -{ - if(session->sessionid) { - /* defensive check */ - - /* free the ID the SSL-layer specific way */ - session->sessionid_free(session->sessionid, session->idsize); - - session->sessionid = NULL; - session->sessionid_free = NULL; - session->age = 0; /* fresh */ - - free_primary_ssl_config(&session->ssl_config); - - Curl_safefree(session->name); - Curl_safefree(session->conn_to_host); - Curl_safefree(session->alpn); - } -} - -/* - * Delete the given session ID from the cache. - */ -void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid) -{ - size_t i; - - for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) { - struct Curl_ssl_session *check = &data->state.session[i]; - - if(check->sessionid == ssl_sessionid) { - Curl_ssl_kill_session(check); - break; - } - } -} - -CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct ssl_peer *peer, - const char *alpn, - void *ssl_sessionid, - size_t idsize, - Curl_ssl_sessionid_dtor *sessionid_free_cb) -{ - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - size_t i; - struct Curl_ssl_session *store; - long oldest_age; - char *clone_host = NULL; - char *clone_conn_to_host = NULL; - char *clone_alpn = NULL; - int conn_to_port; - long *general_age; - void *old_sessionid; - size_t old_size; - CURLcode result = CURLE_OUT_OF_MEMORY; - - DEBUGASSERT(ssl_sessionid); - DEBUGASSERT(sessionid_free_cb); - - if(!data->state.session) { - sessionid_free_cb(ssl_sessionid, idsize); - return CURLE_OK; - } - - if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size, NULL)) { - if((old_size == idsize) && - ((old_sessionid == ssl_sessionid) || - (idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) { - /* the very same */ - sessionid_free_cb(ssl_sessionid, idsize); - return CURLE_OK; - } - Curl_ssl_delsessionid(data, old_sessionid); - } - - store = &data->state.session[0]; - oldest_age = data->state.session[0].age; /* zero if unused */ - DEBUGASSERT(ssl_config->primary.cache_session); - (void)ssl_config; - - clone_host = strdup(peer->hostname); - if(!clone_host) - goto out; - - if(cf->conn->bits.conn_to_host) { - clone_conn_to_host = strdup(cf->conn->conn_to_host.name); - if(!clone_conn_to_host) - goto out; - } - - clone_alpn = alpn ? strdup(alpn) : NULL; - if(alpn && !clone_alpn) - goto out; - - if(cf->conn->bits.conn_to_port) - conn_to_port = cf->conn->conn_to_port; - else - conn_to_port = -1; - - /* Now we should add the session ID and the hostname to the cache, (remove - the oldest if necessary) */ - - /* If using shared SSL session, lock! */ - if(SSLSESSION_SHARED(data)) { - general_age = &data->share->sessionage; - } - else { - general_age = &data->state.sessionage; - } - - /* find an empty slot for us, or find the oldest */ - for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) && - data->state.session[i].sessionid; i++) { - if(data->state.session[i].age < oldest_age) { - oldest_age = data->state.session[i].age; - store = &data->state.session[i]; - } - } - if(i == data->set.general_ssl.max_ssl_sessions) - /* cache is full, we must "kill" the oldest entry! */ - Curl_ssl_kill_session(store); - else - store = &data->state.session[i]; /* use this slot */ - - /* now init the session struct wisely */ - if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { - free_primary_ssl_config(&store->ssl_config); - store->sessionid = NULL; /* let caller free sessionid */ - goto out; - } - store->sessionid = ssl_sessionid; - store->idsize = idsize; - store->sessionid_free = sessionid_free_cb; - store->age = *general_age; /* set current age */ - /* free it if there is one already present */ - free(store->name); - free(store->conn_to_host); - store->name = clone_host; /* clone hostname */ - clone_host = NULL; - store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */ - clone_conn_to_host = NULL; - store->conn_to_port = conn_to_port; /* connect to port number */ - store->alpn = clone_alpn; - clone_alpn = NULL; - /* port number */ - store->remote_port = peer->port; - store->scheme = cf->conn->handler->scheme; - store->transport = peer->transport; - - result = CURLE_OK; - -out: - free(clone_host); - free(clone_conn_to_host); - free(clone_alpn); - if(result) { - failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]", - store->scheme, store->name, store->remote_port, - Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"); - sessionid_free_cb(ssl_sessionid, idsize); - return result; - } - CURL_TRC_CF(data, cf, "Added Session ID to cache for %s://%s:%d [%s]", - store->scheme, store->name, store->remote_port, - Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"); - return CURLE_OK; + return connssl->ssl_impl->connect_nonblocking(cf, data, done); } CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, @@ -788,18 +526,8 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, void Curl_ssl_close_all(struct Curl_easy *data) { - /* kill the session ID cache if not shared */ - if(data->state.session && !SSLSESSION_SHARED(data)) { - size_t i; - for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) - /* the single-killer function handles empty table slots */ - Curl_ssl_kill_session(&data->state.session[i]); - - /* free the cache data */ - Curl_safefree(data->state.session); - } - - Curl_ssl->close_all(data); + if(Curl_ssl->close_all) + Curl_ssl->close_all(data); } void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -828,43 +556,26 @@ void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, */ CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine) { - return Curl_ssl->set_engine(data, engine); + if(Curl_ssl->set_engine) + return Curl_ssl->set_engine(data, engine); + return CURLE_NOT_BUILT_IN; } /* Selects the default SSL crypto engine */ CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data) { - return Curl_ssl->set_engine_default(data); + if(Curl_ssl->set_engine_default) + return Curl_ssl->set_engine_default(data); + return CURLE_NOT_BUILT_IN; } /* Return list of OpenSSL crypto engine names. */ struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data) { - return Curl_ssl->engines_list(data); -} - -/* - * This sets up a session ID cache to the specified size. Make sure this code - * is agnostic to what underlying SSL technology we use. - */ -CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount) -{ - struct Curl_ssl_session *session; - - if(data->state.session) - /* this is just a precaution to prevent multiple inits */ - return CURLE_OK; - - session = calloc(amount, sizeof(struct Curl_ssl_session)); - if(!session) - return CURLE_OUT_OF_MEMORY; - - /* store the info in the SSL section */ - data->set.general_ssl.max_ssl_sessions = amount; - data->state.session = session; - data->state.sessionage = 1; /* this is brand new */ - return CURLE_OK; + if(Curl_ssl->engines_list) + return Curl_ssl->engines_list(data); + return NULL; } static size_t multissl_version(char *buffer, size_t size); @@ -950,7 +661,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, return result; } -/* get 32 bits of random */ +/* get length bytes of randomness */ CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *entropy, size_t length) @@ -1185,96 +896,18 @@ end: */ bool Curl_ssl_cert_status_request(void) { - return Curl_ssl->cert_status_request(); + if(Curl_ssl->cert_status_request) + return Curl_ssl->cert_status_request(); + return FALSE; } /* * Check whether the SSL backend supports false start. */ -bool Curl_ssl_false_start(struct Curl_easy *data) -{ - (void)data; - return Curl_ssl->false_start(); -} - -/* - * Default implementations for unsupported functions. - */ - -int Curl_none_init(void) -{ - return 1; -} - -void Curl_none_cleanup(void) -{ } - -CURLcode Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM, - struct Curl_easy *data UNUSED_PARAM, - bool send_shutdown UNUSED_PARAM, - bool *done) -{ - (void)data; - (void)cf; - (void)send_shutdown; - /* Every SSL backend should have a shutdown implementation. Until we - * have implemented that, we put this fake in place. */ - *done = TRUE; - return CURLE_OK; -} - -int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - (void)cf; - (void)data; - return -1; -} - -void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM) -{ - (void)data; -} - -void Curl_none_session_free(void *ptr UNUSED_PARAM) -{ - (void)ptr; -} - -bool Curl_none_data_pending(struct Curl_cfilter *cf UNUSED_PARAM, - const struct Curl_easy *data UNUSED_PARAM) -{ - (void)cf; - (void)data; - return 0; -} - -bool Curl_none_cert_status_request(void) -{ - return FALSE; -} - -CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM, - const char *engine UNUSED_PARAM) -{ - (void)data; - (void)engine; - return CURLE_NOT_BUILT_IN; -} - -CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM) -{ - (void)data; - return CURLE_NOT_BUILT_IN; -} - -struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM) -{ - (void)data; - return (struct curl_slist *)NULL; -} - -bool Curl_none_false_start(void) +bool Curl_ssl_false_start(void) { + if(Curl_ssl->false_start) + return Curl_ssl->false_start(); return FALSE; } @@ -1351,26 +984,23 @@ static const struct Curl_ssl Curl_ssl_multi = { (size_t)-1, /* something insanely large to be on the safe side */ multissl_init, /* init */ - Curl_none_cleanup, /* cleanup */ + NULL, /* cleanup */ multissl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ + NULL, /* shutdown */ + NULL, /* data_pending */ NULL, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ + NULL, /* cert_status_request */ multissl_connect, /* connect */ multissl_connect_nonblocking, /* connect_nonblocking */ - multissl_adjust_pollset, /* adjust_pollset */ + multissl_adjust_pollset, /* adjust_pollset */ multissl_get_internals, /* get_internals */ multissl_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ + NULL, /* close_all */ + NULL, /* set_engine */ + NULL, /* set_engine_default */ + NULL, /* engines_list */ + NULL, /* false_start */ NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ multissl_recv_plain, /* recv decrypted data */ multissl_send_plain, /* send data to encrypt */ NULL, /* get_channel_binding */ @@ -1432,7 +1062,8 @@ void Curl_ssl_cleanup(void) { if(init_ssl) { /* only cleanup if we did a previous init */ - Curl_ssl->cleanup(); + if(Curl_ssl->cleanup) + Curl_ssl->cleanup(); #if defined(CURL_WITH_MULTI_SSL) Curl_ssl = &Curl_ssl_multi; #endif @@ -1482,8 +1113,8 @@ static size_t multissl_version(char *buffer, size_t size) static int multissl_setup(const struct Curl_ssl *backend) { - const char *env; - char *env_tmp; + int i; + char *env; if(Curl_ssl != &Curl_ssl_multi) return 1; @@ -1496,25 +1127,31 @@ static int multissl_setup(const struct Curl_ssl *backend) if(!available_backends[0]) return 1; - env = env_tmp = curl_getenv("CURL_SSL_BACKEND"); -#ifdef CURL_DEFAULT_SSL_BACKEND - if(!env) - env = CURL_DEFAULT_SSL_BACKEND; -#endif + env = curl_getenv("CURL_SSL_BACKEND"); if(env) { - int i; for(i = 0; available_backends[i]; i++) { if(strcasecompare(env, available_backends[i]->info.name)) { Curl_ssl = available_backends[i]; - free(env_tmp); + free(env); return 0; } } } +#ifdef CURL_DEFAULT_SSL_BACKEND + for(i = 0; available_backends[i]; i++) { + if(strcasecompare(CURL_DEFAULT_SSL_BACKEND, + available_backends[i]->info.name)) { + Curl_ssl = available_backends[i]; + free(env); + return 0; + } + } +#endif + /* Fall back to first available backend */ Curl_ssl = available_backends[0]; - free(env_tmp); + free(env); return 0; } @@ -1565,11 +1202,12 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { + Curl_safefree(peer->sni); if(peer->dispname != peer->hostname) free(peer->dispname); - free(peer->sni); - free(peer->hostname); - peer->hostname = peer->sni = peer->dispname = NULL; + peer->dispname = NULL; + Curl_safefree(peer->hostname); + Curl_safefree(peer->scache_key); peer->type = CURL_SSL_PEER_DNS; } @@ -1577,7 +1215,7 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; if(connssl) { - Curl_ssl->close(cf, data); + connssl->ssl_impl->close(cf, data); connssl->state = ssl_connection_none; Curl_ssl_peer_cleanup(&connssl->peer); } @@ -1603,7 +1241,9 @@ static ssl_peer_type get_peer_type(const char *hostname) return CURL_SSL_PEER_DNS; } -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, + struct Curl_cfilter *cf, + const char *tls_id, int transport) { const char *ehostname, *edispname; @@ -1665,7 +1305,8 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, peer->sni[len] = 0; } } - result = CURLE_OK; + + result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key); out: if(result) @@ -1728,7 +1369,9 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, *done = FALSE; if(!connssl->peer.hostname) { - result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP); + char tls_id[80]; + connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); + result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP); if(result) goto out; } @@ -1743,7 +1386,8 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, if(!result && *done) { cf->connected = TRUE; - connssl->handshake_done = Curl_now(); + if(connssl->state == ssl_connection_complete) + connssl->handshake_done = Curl_now(); /* Connection can be deferred when sending early data */ DEBUGASSERT(connssl->state == ssl_connection_complete || connssl->state == ssl_connection_deferred); @@ -1757,11 +1401,13 @@ out: static bool ssl_cf_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { + struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; bool result; CF_DATA_SAVE(save, cf, data); - if(Curl_ssl->data_pending(cf, data)) + if(connssl->ssl_impl->data_pending && + connssl->ssl_impl->data_pending(cf, data)) result = TRUE; else result = cf->next->cft->has_data_pending(cf->next, data); @@ -1773,6 +1419,7 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, bool eos, CURLcode *err) { + struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; ssize_t nwritten = 0; @@ -1781,7 +1428,7 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, *err = CURLE_OK; if(len > 0) { CF_DATA_SAVE(save, cf, data); - nwritten = Curl_ssl->send_plain(cf, data, buf, len, err); + nwritten = connssl->ssl_impl->send_plain(cf, data, buf, len, err); CF_DATA_RESTORE(cf, save); } return nwritten; @@ -1791,12 +1438,13 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err) { + struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; ssize_t nread; CF_DATA_SAVE(save, cf, data); *err = CURLE_OK; - nread = Curl_ssl->recv_plain(cf, data, buf, len, err); + nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err); if(nread > 0) { DEBUGASSERT((size_t)nread <= len); } @@ -1814,14 +1462,15 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { + struct ssl_connect_data *connssl = cf->ctx; CURLcode result = CURLE_OK; *done = TRUE; - if(!cf->shutdown) { + if(!cf->shutdown && Curl_ssl->shut_down) { struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->shut_down(cf, data, TRUE, done); + result = connssl->ssl_impl->shut_down(cf, data, TRUE, done); CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done); CF_DATA_RESTORE(cf, save); cf->shutdown = (result || *done); @@ -1833,42 +1482,14 @@ static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) { + struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - Curl_ssl->adjust_pollset(cf, data, ps); + connssl->ssl_impl->adjust_pollset(cf, data, ps); CF_DATA_RESTORE(cf, save); } -static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - struct cf_call_data save; - - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_DATA_ATTACH: - if(Curl_ssl->attach_data) { - CF_DATA_SAVE(save, cf, data); - Curl_ssl->attach_data(cf, data); - CF_DATA_RESTORE(cf, save); - } - break; - case CF_CTRL_DATA_DETACH: - if(Curl_ssl->detach_data) { - CF_DATA_SAVE(save, cf, data); - Curl_ssl->detach_data(cf, data); - CF_DATA_RESTORE(cf, save); - } - break; - default: - break; - } - return CURLE_OK; -} - static CURLcode ssl_cf_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) @@ -1893,28 +1514,9 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf, static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, bool *input_pending) { - struct cf_call_data save; - int result; /* * This function tries to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown */ - CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->check_cxn(cf, data); - CF_DATA_RESTORE(cf, save); - if(result > 0) { - *input_pending = TRUE; - return TRUE; - } - if(result == 0) { - *input_pending = FALSE; - return FALSE; - } - /* ssl backend does not know */ return cf->next ? cf->next->cft->is_alive(cf->next, data, input_pending) : FALSE; /* pessimistic in absence of data */ @@ -1933,7 +1535,7 @@ struct Curl_cftype Curl_cft_ssl = { ssl_cf_data_pending, ssl_cf_send, ssl_cf_recv, - ssl_cf_cntrl, + Curl_cf_def_cntrl, cf_ssl_is_alive, Curl_cf_def_conn_keep_alive, ssl_cf_query, @@ -1954,7 +1556,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = { ssl_cf_data_pending, ssl_cf_send, ssl_cf_recv, - ssl_cf_cntrl, + Curl_cf_def_cntrl, cf_ssl_is_alive, Curl_cf_def_conn_keep_alive, Curl_cf_def_query, @@ -2090,9 +1692,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, /* get first SSL filter in chain, if any is present */ cf = get_ssl_filter(data->conn->cfilter[sockindex]); if(cf) { + struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->get_internals(cf->ctx, info); + result = connssl->ssl_impl->get_internals(cf->ctx, info); CF_DATA_RESTORE(cf, save); } } @@ -2125,7 +1728,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - result = Curl_ssl->shut_down(cf, data, send_shutdown, done); + result = connssl->ssl_impl->shut_down(cf, data, send_shutdown, done); if(result ||*done) goto out; @@ -2277,28 +1880,28 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, #endif ; - if(connssl->alpn_negotiated) { + if(connssl->negotiated.alpn) { /* When we ask for a specific ALPN protocol, we need the confirmation * of it by the server, as we have installed protocol handler and * connection filter chain for exactly this protocol. */ if(!proto_len) { failf(data, "ALPN: asked for '%s' from previous session, " "but server did not confirm it. Refusing to continue.", - connssl->alpn_negotiated); + connssl->negotiated.alpn); result = CURLE_SSL_CONNECT_ERROR; goto out; } - else if((strlen(connssl->alpn_negotiated) != proto_len) || - memcmp(connssl->alpn_negotiated, proto, proto_len)) { + else if((strlen(connssl->negotiated.alpn) != proto_len) || + memcmp(connssl->negotiated.alpn, proto, proto_len)) { failf(data, "ALPN: asked for '%s' from previous session, but server " "selected '%.*s'. Refusing to continue.", - connssl->alpn_negotiated, (int)proto_len, proto); + connssl->negotiated.alpn, (int)proto_len, proto); result = CURLE_SSL_CONNECT_ERROR; goto out; } /* ALPN is exactly what we asked for, done. */ infof(data, "ALPN: server confirmed to use '%s'", - connssl->alpn_negotiated); + connssl->negotiated.alpn); goto out; } @@ -2309,11 +1912,11 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, result = CURLE_SSL_CONNECT_ERROR; goto out; } - connssl->alpn_negotiated = malloc(proto_len + 1); - if(!connssl->alpn_negotiated) + connssl->negotiated.alpn = malloc(proto_len + 1); + if(!connssl->negotiated.alpn) return CURLE_OUT_OF_MEMORY; - memcpy(connssl->alpn_negotiated, proto, proto_len); - connssl->alpn_negotiated[proto_len] = 0; + memcpy(connssl->negotiated.alpn, proto, proto_len); + connssl->negotiated.alpn[proto_len] = 0; } if(proto && proto_len) { diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 7a223f6..b751c37 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -28,7 +28,9 @@ struct connectdata; struct ssl_config_data; struct ssl_primary_config; -struct Curl_ssl_session; +struct Curl_cfilter; +struct Curl_easy; +struct dynbuf; #define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */ #define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */ @@ -41,6 +43,14 @@ struct Curl_ssl_session; #define SSLSUPP_CA_CACHE (1<<8) #define SSLSUPP_CIPHER_LIST (1<<9) /* supports TLS 1.0-1.2 ciphersuites */ +#ifdef USE_ECH +# include "curl_base64.h" +# define ECH_ENABLED(__data__) \ + (__data__->set.tls_ech && \ + !(__data__->set.tls_ech & CURLECH_DISABLE)\ + ) +#endif /* USE_ECH */ + #define ALPN_ACCEPTED "ALPN: server accepted " #define VTLS_INFOF_NO_ALPN \ @@ -55,9 +65,31 @@ struct Curl_ssl_session; #define VTLS_INFOF_ALPN_DEFERRED \ "ALPN: deferred handshake for early data using '%.*s'." -/* Curl_multi SSL backend-specific data; declared differently by each SSL - backend */ -struct Curl_cfilter; +/* IETF defined version numbers used in TLS protocol negotiation */ +#define CURL_IETF_PROTO_UNKNOWN 0x0 +#define CURL_IETF_PROTO_SSL3 0x0300 +#define CURL_IETF_PROTO_TLS1 0x0301 +#define CURL_IETF_PROTO_TLS1_1 0x0302 +#define CURL_IETF_PROTO_TLS1_2 0x0303 +#define CURL_IETF_PROTO_TLS1_3 0x0304 +#define CURL_IETF_PROTO_DTLS1 0xFEFF +#define CURL_IETF_PROTO_DTLS1_2 0xFEFD + +typedef enum { + CURL_SSL_PEER_DNS, + CURL_SSL_PEER_IPV4, + CURL_SSL_PEER_IPV6 +} ssl_peer_type; + +struct ssl_peer { + char *hostname; /* hostname for verification */ + char *dispname; /* display version of hostname */ + char *sni; /* SNI version of hostname or NULL if not usable */ + char *scache_key; /* for lookups in session cache */ + ssl_peer_type type; /* type of the peer information */ + int port; /* port we are talking to */ + int transport; /* one of TRNSPRT_* defines */ +}; CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, const curl_ssl_backend ***avail); @@ -113,7 +145,9 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); * Init SSL peer information for filter. Can be called repeatedly. */ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, - struct Curl_cfilter *cf, int transport); + struct Curl_cfilter *cf, + const char *tls_id, + int transport); /** * Free all allocated data and reset peer information. */ @@ -130,8 +164,6 @@ CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine); CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data); struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data); -/* init the SSL session ID cache */ -CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t); void Curl_ssl_version(char *buffer, size_t size); /* Certificate information list handling. */ @@ -147,33 +179,6 @@ CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum, /* Functions to be used by SSL library adaptation functions */ -/* Lock session cache mutex. - * Call this before calling other Curl_ssl_*session* functions - * Caller should unlock this mutex as soon as possible, as it may block - * other SSL connection from making progress. - * The purpose of explicitly locking SSL session cache data is to allow - * individual SSL engines to manage session lifetime in their specific way. - */ -void Curl_ssl_sessionid_lock(struct Curl_easy *data); - -/* Unlock session cache mutex */ -void Curl_ssl_sessionid_unlock(struct Curl_easy *data); - -/* Kill a single session ID entry in the cache - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * This will call engine-specific curlssl_session_free function, which must - * take sessionid object ownership from sessionid cache - * (e.g. decrement refcount). - */ -void Curl_ssl_kill_session(struct Curl_ssl_session *session); -/* delete a session from the cache - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * This will call engine-specific curlssl_session_free function, which must - * take sessionid object ownership from sessionid cache - * (e.g. decrement refcount). - */ -void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid); - /* get N random bytes into the buffer */ CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer, size_t length); @@ -184,7 +189,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, bool Curl_ssl_cert_status_request(void); -bool Curl_ssl_false_start(struct Curl_easy *data); +bool Curl_ssl_false_start(void); /* The maximum size of the SSL channel binding is 85 bytes, as defined in * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long, @@ -265,12 +270,10 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN #define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN #define Curl_ssl_engines_list(x) NULL -#define Curl_ssl_initsessions(x,y) CURLE_OK #define Curl_ssl_free_certinfo(x) Curl_nop_stmt -#define Curl_ssl_kill_session(x) Curl_nop_stmt #define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN) #define Curl_ssl_cert_status_request() FALSE -#define Curl_ssl_false_start(a) FALSE +#define Curl_ssl_false_start() FALSE #define Curl_ssl_get_internals(a,b,c,d) NULL #define Curl_ssl_supports(a,b) FALSE #define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 13bd3fb..3a5611d 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -26,9 +26,11 @@ #include "curl_setup.h" #include "cfilters.h" #include "urldata.h" +#include "vtls.h" #ifdef USE_SSL +struct Curl_ssl; struct ssl_connect_data; /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ @@ -103,12 +105,15 @@ typedef enum { /* Information in each SSL cfilter context: cf->ctx */ struct ssl_connect_data { - struct ssl_peer peer; + const struct Curl_ssl *ssl_impl; /* TLS backend for this filter */ + struct ssl_peer peer; /* peer the filter talks to */ const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ - char *alpn_negotiated; /* negotiated ALPN value or NULL */ + struct { + char *alpn; /* ALPN value or NULL */ + } negotiated; struct bufq earlydata; /* earlydata to be send to peer */ size_t earlydata_max; /* max earlydata allowed by peer */ size_t earlydata_skip; /* sending bytes to skip when earlydata @@ -142,7 +147,6 @@ struct Curl_ssl { void (*cleanup)(void); size_t (*version)(char *buffer, size_t size); - int (*check_cxn)(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode (*shut_down)(struct Curl_cfilter *cf, struct Curl_easy *data, bool send_shutdown, bool *done); bool (*data_pending)(struct Curl_cfilter *cf, @@ -174,10 +178,6 @@ struct Curl_ssl { bool (*false_start)(void); CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256sumlen); - - bool (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data); - void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data); - ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *code); ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -190,66 +190,14 @@ struct Curl_ssl { extern const struct Curl_ssl *Curl_ssl; - -int Curl_none_init(void); -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); -void Curl_none_close_all(struct Curl_easy *data); -void Curl_none_session_free(void *ptr); -bool Curl_none_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data); -bool Curl_none_cert_status_request(void); -CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine); -CURLcode Curl_none_set_engine_default(struct Curl_easy *data); -struct curl_slist *Curl_none_engines_list(struct Curl_easy *data); -bool Curl_none_false_start(void); void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - struct easy_pollset *ps); + struct easy_pollset *ps); /** * Get the SSL filter below the given one or NULL if there is none. */ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf); -/* extract a session ID - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * Caller must make sure that the ownership of returned sessionid object - * is properly taken (e.g. its refcount is incremented - * under sessionid mutex). - * @param cf the connection filter wanting to use it - * @param data the transfer involved - * @param peer the peer the filter wants to talk to - * @param sessionid on return the TLS session - * @param idsize on return the size of the TLS session data - * @param palpn on return the ALPN string used by the session, - * set to NULL when not interested - */ -bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct ssl_peer *peer, - void **ssl_sessionid, - size_t *idsize, /* set 0 if unknown */ - char **palpn); - -/* Set a TLS session ID for `peer`. Replaces an existing session ID if - * not already the same. - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb` - * to deallocate it. Is called in all outcomes, either right away or - * later when the session cache is cleaned up. - * Caller must ensure that it has properly shared ownership of this sessionid - * object with cache (e.g. incrementing refcount on success) - */ -CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct ssl_peer *peer, - const char *alpn, - void *sessionid, - size_t sessionid_size, - Curl_ssl_sessionid_dtor *sessionid_free_cb); - #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */ diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c new file mode 100644 index 0000000..9c04b77 --- /dev/null +++ b/lib/vtls/vtls_scache.c @@ -0,0 +1,1170 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_SSL + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "urldata.h" +#include "cfilters.h" + +#include "vtls.h" /* generic SSL protos etc */ +#include "vtls_int.h" +#include "vtls_scache.h" +#include "vtls_spack.h" + +#include "strcase.h" +#include "url.h" +#include "llist.h" +#include "share.h" +#include "curl_trc.h" +#include "curl_sha256.h" +#include "rand.h" +#include "warnless.h" +#include "curl_printf.h" +#include "strdup.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* a peer+tls-config we cache sessions for */ +struct Curl_ssl_scache_peer { + char *ssl_peer_key; /* id for peer + relevant TLS configuration */ + char *clientcert; + char *srp_username; + char *srp_password; + struct Curl_llist sessions; + void *sobj; /* object instance or NULL */ + Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */ + unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */ + unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */ + size_t max_sessions; + long age; /* just a number, the higher the more recent */ + BIT(hmac_set); /* if key_salt and key_hmac are present */ +}; + +struct Curl_ssl_scache { + struct Curl_ssl_scache_peer *peers; + size_t peer_count; + int default_lifetime_secs; + long age; +}; + +static void cf_ssl_scache_clear_session(struct Curl_ssl_session *s) +{ + if(s->sdata) { + free((void *)s->sdata); + s->sdata = NULL; + } + s->sdata_len = 0; + if(s->quic_tp) { + free((void *)s->quic_tp); + s->quic_tp = NULL; + } + s->quic_tp_len = 0; + s->ietf_tls_id = 0; + s->valid_until = 0; + Curl_safefree(s->alpn); +} + +static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *s) +{ + (void)udata; + cf_ssl_scache_clear_session(s); + free(s); +} + +CURLcode +Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len, + int ietf_tls_id, const char *alpn, + curl_off_t valid_until, size_t earlydata_max, + struct Curl_ssl_session **psession) +{ + return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn, + valid_until, earlydata_max, + NULL, 0, psession); +} + +CURLcode +Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len, + int ietf_tls_id, const char *alpn, + curl_off_t valid_until, size_t earlydata_max, + unsigned char *quic_tp, size_t quic_tp_len, + struct Curl_ssl_session **psession) +{ + struct Curl_ssl_session *s; + + if(!sdata || !sdata_len) { + free(sdata); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + *psession = NULL; + s = calloc(1, sizeof(*s)); + if(!s) { + free(sdata); + free(quic_tp); + return CURLE_OUT_OF_MEMORY; + } + + s->ietf_tls_id = ietf_tls_id; + s->valid_until = valid_until; + s->earlydata_max = earlydata_max; + s->sdata = sdata; + s->sdata_len = sdata_len; + s->quic_tp = quic_tp; + s->quic_tp_len = quic_tp_len; + if(alpn) { + s->alpn = strdup(alpn); + if(!s->alpn) { + cf_ssl_scache_sesssion_ldestroy(NULL, s); + return CURLE_OUT_OF_MEMORY; + } + } + *psession = s; + return CURLE_OK; +} + +void Curl_ssl_session_destroy(struct Curl_ssl_session *s) +{ + if(s) { + /* if in the list, the list destructor takes care of it */ + if(Curl_node_llist(&s->list)) + Curl_node_remove(&s->list); + else { + cf_ssl_scache_sesssion_ldestroy(NULL, s); + } + } +} + +static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) +{ + Curl_llist_destroy(&peer->sessions, NULL); + if(peer->sobj) { + DEBUGASSERT(peer->sobj_free); + if(peer->sobj_free) + peer->sobj_free(peer->sobj); + peer->sobj = NULL; + } + peer->sobj_free = NULL; + Curl_safefree(peer->clientcert); +#ifdef USE_TLS_SRP + Curl_safefree(peer->srp_username); + Curl_safefree(peer->srp_password); +#endif + Curl_safefree(peer->ssl_peer_key); + peer->age = 0; + peer->hmac_set = FALSE; +} + +static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer, + void *sobj, + Curl_ssl_scache_obj_dtor *sobj_free) +{ + DEBUGASSERT(peer); + if(peer->sobj_free) { + peer->sobj_free(peer->sobj); + } + peer->sobj = sobj; + peer->sobj_free = sobj_free; +} + +static CURLcode +cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, + const char *ssl_peer_key, + const char *clientcert, + const char *srp_username, + const char *srp_password, + const unsigned char *salt, + const unsigned char *hmac) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(!peer->ssl_peer_key); + if(ssl_peer_key) { + peer->ssl_peer_key = strdup(ssl_peer_key); + if(!peer->ssl_peer_key) + goto out; + peer->hmac_set = FALSE; + } + else if(salt && hmac) { + memcpy(peer->key_salt, salt, sizeof(peer->key_salt)); + memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac)); + peer->hmac_set = TRUE; + } + else { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + if(clientcert) { + peer->clientcert = strdup(clientcert); + if(!peer->clientcert) + goto out; + } + if(srp_username) { + peer->srp_username = strdup(srp_username); + if(!peer->srp_username) + goto out; + } + if(srp_password) { + peer->srp_password = strdup(srp_password); + if(!peer->srp_password) + goto out; + } + result = CURLE_OK; +out: + if(result) + cf_ssl_scache_clear_peer(peer); + return result; +} + +static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer, + struct Curl_ssl_session *s) +{ + (void)peer; + DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions); + Curl_ssl_session_destroy(s); +} + +static bool cf_scache_session_expired(struct Curl_ssl_session *s, + curl_off_t now) +{ + return (s->valid_until > 0) && (s->valid_until < now); +} + +static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer, + curl_off_t now) +{ + struct Curl_llist_node *n = Curl_llist_head(&peer->sessions); + while(n) { + struct Curl_ssl_session *s = Curl_node_elem(n); + n = Curl_node_next(n); + if(cf_scache_session_expired(s, now)) + cf_scache_session_remove(peer, s); + } +} + +static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer) +{ + struct Curl_llist_node *n = Curl_llist_head(&peer->sessions); + while(n) { + struct Curl_ssl_session *s = Curl_node_elem(n); + n = Curl_node_next(n); + if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) + cf_scache_session_remove(peer, s); + } +} + +CURLcode Curl_ssl_scache_create(size_t max_peers, + size_t max_sessions_per_peer, + struct Curl_ssl_scache **pscache) +{ + struct Curl_ssl_scache *scache; + struct Curl_ssl_scache_peer *peers; + size_t i; + + *pscache = NULL; + peers = calloc(max_peers, sizeof(*peers)); + if(!peers) + return CURLE_OUT_OF_MEMORY; + + scache = calloc(1, sizeof(*scache)); + if(!scache) { + free(peers); + return CURLE_OUT_OF_MEMORY; + } + + scache->default_lifetime_secs = (24*60*60); /* 1 day */ + scache->peer_count = max_peers; + scache->peers = peers; + scache->age = 1; + for(i = 0; i < scache->peer_count; ++i) { + scache->peers[i].max_sessions = max_sessions_per_peer; + Curl_llist_init(&scache->peers[i].sessions, + cf_ssl_scache_sesssion_ldestroy); + } + + *pscache = scache; + return CURLE_OK; +} + +void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache) +{ + if(scache) { + size_t i; + for(i = 0; i < scache->peer_count; ++i) { + cf_ssl_scache_clear_peer(&scache->peers[i]); + } + free(scache->peers); + free(scache); + } +} + +/* Lock shared SSL session data */ +void Curl_ssl_scache_lock(struct Curl_easy *data) +{ + if(CURL_SHARE_ssl_scache(data)) + Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); +} + +/* Unlock shared SSL session data */ +void Curl_ssl_scache_unlock(struct Curl_easy *data) +{ + if(CURL_SHARE_ssl_scache(data)) + Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); +} + +static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, + const char *name, + char *path) +{ + if(path && path[0]) { + /* We try to add absolute paths, so that the session key can stay + * valid when used in another process with different CWD. However, + * when a path does not exist, this does not work. Then, we add + * the path as is. */ +#ifdef _WIN32 + char abspath[_MAX_PATH]; + if(_fullpath(abspath, path, _MAX_PATH)) + return Curl_dyn_addf(buf, ":%s-%s", name, abspath); +#else + if(path[0] != '/') { + char *abspath = realpath(path, NULL); + if(abspath) { + CURLcode r = Curl_dyn_addf(buf, ":%s-%s", name, abspath); + (free)(abspath); /* allocated by libc, free without memdebug */ + return r; + } + } +#endif + return Curl_dyn_addf(buf, ":%s-%s", name, path); + } + return CURLE_OK; +} + +static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, + const char *name, + struct curl_blob *blob) +{ + CURLcode r = CURLE_OK; + if(blob && blob->len) { + unsigned char hash[CURL_SHA256_DIGEST_LENGTH]; + size_t i; + + r = Curl_dyn_addf(buf, ":%s-", name); + if(r) + goto out; + r = Curl_sha256it(hash, blob->data, blob->len); + if(r) + goto out; + for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) { + r = Curl_dyn_addf(buf, "%02x", hash[i]); + if(r) + goto out; + } + } +out: + return r; +} + +CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, + const struct ssl_peer *peer, + const char *tls_id, + char **ppeer_key) +{ + struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); + struct dynbuf buf; + size_t key_len; + CURLcode r; + + *ppeer_key = NULL; + Curl_dyn_init(&buf, 10 * 1024); + + r = Curl_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); + if(r) + goto out; + + switch(peer->transport) { + case TRNSPRT_TCP: + break; + case TRNSPRT_UDP: + r = Curl_dyn_add(&buf, ":UDP"); + break; + case TRNSPRT_QUIC: + r = Curl_dyn_add(&buf, ":QUIC"); + break; + case TRNSPRT_UNIX: + r = Curl_dyn_add(&buf, ":UNIX"); + break; + default: + r = Curl_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); + break; + } + if(r) + goto out; + + if(!ssl->verifypeer) { + r = Curl_dyn_add(&buf, ":NO-VRFY-PEER"); + if(r) + goto out; + } + if(!ssl->verifyhost) { + r = Curl_dyn_add(&buf, ":NO-VRFY-HOST"); + if(r) + goto out; + } + if(ssl->verifystatus) { + r = Curl_dyn_add(&buf, ":VRFY-STATUS"); + if(r) + goto out; + } + if(!ssl->verifypeer || !ssl->verifyhost) { + if(cf->conn->bits.conn_to_host) { + r = Curl_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); + if(r) + goto out; + } + if(cf->conn->bits.conn_to_port) { + r = Curl_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); + if(r) + goto out; + } + } + + if(ssl->version || ssl->version_max) { + r = Curl_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version, + (ssl->version_max >> 16)); + if(r) + goto out; + } + if(ssl->ssl_options) { + r = Curl_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); + if(r) + goto out; + } + if(ssl->cipher_list) { + r = Curl_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); + if(r) + goto out; + } + if(ssl->cipher_list13) { + r = Curl_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); + if(r) + goto out; + } + if(ssl->curves) { + r = Curl_dyn_addf(&buf, ":CURVES-%s", ssl->curves); + if(r) + goto out; + } + if(ssl->verifypeer) { + r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert); + if(r) + goto out; + if(ssl->cert_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); + if(r) + goto out; + } + if(ssl->ca_info_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); + if(r) + goto out; + } + if(ssl->issuercert_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob); + if(r) + goto out; + } + } + if(ssl->pinned_key && ssl->pinned_key[0]) { + r = Curl_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); + if(r) + goto out; + } + + if(ssl->clientcert && ssl->clientcert[0]) { + r = Curl_dyn_add(&buf, ":CCERT"); + if(r) + goto out; + } +#ifdef USE_TLS_SRP + if(ssl->username || ssl->password) { + r = Curl_dyn_add(&buf, ":SRP-AUTH"); + if(r) + goto out; + } +#endif + + if(!tls_id || !tls_id[0]) { + r = CURLE_FAILED_INIT; + goto out; + } + r = Curl_dyn_addf(&buf, ":IMPL-%s", tls_id); + if(r) + goto out; + + *ppeer_key = Curl_dyn_take(&buf, &key_len); + /* we just added printable char, and dynbuf always 0 terminates, + * no need to track length */ + + +out: + Curl_dyn_free(&buf); + return r; +} + +static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, + struct ssl_primary_config *conn_config) +{ + if(!conn_config) { + if(peer->clientcert) + return FALSE; +#ifdef USE_TLS_SRP + if(peer->srp_username || peer->srp_password) + return FALSE; +#endif + return TRUE; + } + else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert)) + return FALSE; +#ifdef USE_TLS_SRP + if(Curl_timestrcmp(peer->srp_username, conn_config->username) || + Curl_timestrcmp(peer->srp_password, conn_config->password)) + return FALSE; +#endif + return TRUE; +} + +static CURLcode +cf_ssl_find_peer_by_key(struct Curl_easy *data, + struct Curl_ssl_scache *scache, + const char *ssl_peer_key, + struct ssl_primary_config *conn_config, + struct Curl_ssl_scache_peer **ppeer) +{ + size_t i, peer_key_len = 0; + CURLcode result = CURLE_OK; + + *ppeer = NULL; + /* check for entries with known peer_key */ + for(i = 0; scache && i < scache->peer_count; i++) { + if(scache->peers[i].ssl_peer_key && + strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) && + cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) { + /* yes, we have a cached session for this! */ + *ppeer = &scache->peers[i]; + goto out; + } + } + /* check for entries with HMAC set but no known peer_key */ + for(i = 0; scache && i < scache->peer_count; i++) { + if(!scache->peers[i].ssl_peer_key && + scache->peers[i].hmac_set && + cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) { + /* possible entry with unknown peer_key, check hmac */ + unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH]; + if(!peer_key_len) /* we are lazy */ + peer_key_len = strlen(ssl_peer_key); + result = Curl_hmacit(&Curl_HMAC_SHA256, + scache->peers[i].key_salt, + sizeof(scache->peers[i].key_salt), + (const unsigned char *)ssl_peer_key, + peer_key_len, + my_hmac); + if(result) + goto out; + if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) { + /* remember peer_key for future lookups */ + CURL_TRC_SSLS(data, "peer entry %zu key recovered: %s", + i, ssl_peer_key); + scache->peers[i].ssl_peer_key = strdup(ssl_peer_key); + if(!scache->peers[i].ssl_peer_key) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *ppeer = &scache->peers[i]; + goto out; + } + } + } + CURL_TRC_SSLS(data, "peer not found for %s", ssl_peer_key); +out: + return result; +} + +static struct Curl_ssl_scache_peer * +cf_ssl_get_free_peer(struct Curl_ssl_scache *scache) +{ + struct Curl_ssl_scache_peer *peer = NULL; + size_t i; + + /* find empty or oldest peer */ + for(i = 0; i < scache->peer_count; ++i) { + /* free peer entry? */ + if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) { + peer = &scache->peers[i]; + break; + } + /* peer without sessions and obj */ + if(!scache->peers[i].sobj && + !Curl_llist_count(&scache->peers[i].sessions)) { + peer = &scache->peers[i]; + break; + } + /* remember "oldest" peer */ + if(!peer || (scache->peers[i].age < peer->age)) { + peer = &scache->peers[i]; + } + } + DEBUGASSERT(peer); + if(peer) + cf_ssl_scache_clear_peer(peer); + return peer; +} + +static CURLcode +cf_ssl_add_peer(struct Curl_easy *data, + struct Curl_ssl_scache *scache, + const char *ssl_peer_key, + struct ssl_primary_config *conn_config, + struct Curl_ssl_scache_peer **ppeer) +{ + struct Curl_ssl_scache_peer *peer = NULL; + CURLcode result = CURLE_OK; + + *ppeer = NULL; + if(ssl_peer_key) { + result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config, + &peer); + if(result || !scache->peer_count) + return result; + } + + if(peer) { + *ppeer = peer; + return CURLE_OK; + } + + peer = cf_ssl_get_free_peer(scache); + if(peer) { + const char *ccert = conn_config ? conn_config->clientcert : NULL; + const char *username = NULL, *password = NULL; +#ifdef USE_TLS_SRP + username = conn_config ? conn_config->username : NULL; + password = conn_config ? conn_config->password : NULL; +#endif + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, + username, password, NULL, NULL); + if(result) + goto out; + /* all ready */ + *ppeer = peer; + result = CURLE_OK; + } + +out: + if(result) { + cf_ssl_scache_clear_peer(peer); + } + return result; +} + +static void cf_scache_peer_add_session(struct Curl_ssl_scache_peer *peer, + struct Curl_ssl_session *s, + curl_off_t now) +{ + /* A session not from TLSv1.3 replaces all other. */ + if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) { + Curl_llist_destroy(&peer->sessions, NULL); + Curl_llist_append(&peer->sessions, s, &s->list); + } + else { + /* Expire existing, append, trim from head to obey max_sessions */ + cf_scache_peer_remove_expired(peer, now); + cf_scache_peer_remove_non13(peer); + Curl_llist_append(&peer->sessions, s, &s->list); + while(Curl_llist_count(&peer->sessions) > peer->max_sessions) { + Curl_node_remove(Curl_llist_head(&peer->sessions)); + } + } +} + +static CURLcode cf_scache_add_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_ssl_scache *scache, + const char *ssl_peer_key, + struct Curl_ssl_session *s) +{ + struct Curl_ssl_scache_peer *peer = NULL; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + CURLcode result = CURLE_OUT_OF_MEMORY; + curl_off_t now = (curl_off_t)time(NULL); + curl_off_t max_lifetime; + + if(!scache || !scache->peer_count) { + Curl_ssl_session_destroy(s); + return CURLE_OK; + } + + if(s->valid_until <= 0) + s->valid_until = now + scache->default_lifetime_secs; + + max_lifetime = (s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) ? + CURL_SCACHE_MAX_13_LIFETIME_SEC : + CURL_SCACHE_MAX_12_LIFETIME_SEC; + if(s->valid_until > (now + max_lifetime)) + s->valid_until = now + max_lifetime; + + if(cf_scache_session_expired(s, now)) { + CURL_TRC_SSLS(data, "add, session already expired"); + Curl_ssl_session_destroy(s); + return CURLE_OK; + } + + result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer); + if(result || !peer) { + CURL_TRC_SSLS(data, "unable to add scache peer: %d", result); + Curl_ssl_session_destroy(s); + goto out; + } + + cf_scache_peer_add_session(peer, s, now); + +out: + if(result) { + failf(data, "[SCACHE] failed to add session for %s, error=%d", + ssl_peer_key, result); + } + else + CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, " + "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, " + "quic_tp=%s], peer has %zu sessions now", + ssl_peer_key, s->ietf_tls_id, s->valid_until - now, + s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no", + peer ? Curl_llist_count(&peer->sessions) : 0); + return result; +} + +CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + struct Curl_ssl_session *s) +{ + struct Curl_ssl_scache *scache = data->state.ssl_scache; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result; + DEBUGASSERT(ssl_config); + + if(!scache || !ssl_config->primary.cache_session) { + Curl_ssl_session_destroy(s); + return CURLE_OK; + } + + Curl_ssl_scache_lock(data); + result = cf_scache_add_session(cf, data, scache, ssl_peer_key, s); + Curl_ssl_scache_unlock(data); + return result; +} + +void Curl_ssl_scache_return(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + struct Curl_ssl_session *s) +{ + /* See RFC 8446 C.4: + * "Clients SHOULD NOT reuse a ticket for multiple connections." */ + if(s && s->ietf_tls_id < 0x304) + (void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s); + else + Curl_ssl_session_destroy(s); +} + +CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + struct Curl_ssl_session **ps) +{ + struct Curl_ssl_scache *scache = data->state.ssl_scache; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_ssl_scache_peer *peer = NULL; + struct Curl_llist_node *n; + struct Curl_ssl_session *s = NULL; + CURLcode result; + + *ps = NULL; + if(!scache) + return CURLE_OK; + + Curl_ssl_scache_lock(data); + result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config, + &peer); + if(!result && peer) { + cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL)); + n = Curl_llist_head(&peer->sessions); + if(n) { + s = Curl_node_take_elem(n); + (scache->age)++; /* increase general age */ + peer->age = scache->age; /* set this as used in this age */ + } + } + Curl_ssl_scache_unlock(data); + if(s) { + *ps = s; + CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, " + "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain", + ssl_peer_key, s->ietf_tls_id, s->alpn, + s->earlydata_max, s->quic_tp ? "yes" : "no", + Curl_llist_count(&peer->sessions)); + } + else { + CURL_TRC_SSLS(data, "no cached session for %s", ssl_peer_key); + } + return result; +} + +CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + void *sobj, + Curl_ssl_scache_obj_dtor *sobj_free) +{ + struct Curl_ssl_scache *scache = data->state.ssl_scache; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_ssl_scache_peer *peer = NULL; + CURLcode result; + + DEBUGASSERT(sobj); + DEBUGASSERT(sobj_free); + + result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer); + if(result || !peer) { + CURL_TRC_SSLS(data, "unable to add scache peer: %d", result); + goto out; + } + + cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free); + sobj = NULL; /* peer took ownership */ + +out: + if(sobj && sobj_free) + sobj_free(sobj); + return result; +} + +bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + void **sobj) +{ + struct Curl_ssl_scache *scache = data->state.ssl_scache; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_ssl_scache_peer *peer = NULL; + CURLcode result; + + *sobj = NULL; + if(!scache) + return FALSE; + + result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config, + &peer); + if(result) + return FALSE; + + if(peer) + *sobj = peer->sobj; + + CURL_TRC_SSLS(data, "%s cached session for '%s'", + *sobj ? "Found" : "No", ssl_peer_key); + return !!*sobj; +} + +void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key) +{ + struct Curl_ssl_scache *scache = data->state.ssl_scache; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_ssl_scache_peer *peer = NULL; + CURLcode result; + + (void)cf; + if(!scache) + return; + + Curl_ssl_scache_lock(data); + result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config, + &peer); + if(!result && peer) + cf_ssl_scache_clear_peer(peer); + Curl_ssl_scache_unlock(data); +} + +#ifdef USE_SSLS_EXPORT + +#define CURL_SSL_TICKET_MAX (16*1024) + +static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer) +{ + CURLcode result; + + DEBUGASSERT(peer); + if(!peer->ssl_peer_key) + return CURLE_BAD_FUNCTION_ARGUMENT; + + result = Curl_rand(NULL, peer->key_salt, sizeof(peer->key_salt)); + if(result) + return result; + + result = Curl_hmacit(&Curl_HMAC_SHA256, + peer->key_salt, sizeof(peer->key_salt), + (const unsigned char *)peer->ssl_peer_key, + strlen(peer->ssl_peer_key), + peer->key_hmac); + if(!result) + peer->hmac_set = TRUE; + return result; +} + +static CURLcode +cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache, + const unsigned char *salt, + const unsigned char *hmac, + struct Curl_ssl_scache_peer **ppeer) +{ + size_t i; + CURLcode result = CURLE_OK; + + *ppeer = NULL; + /* look for an entry that matches salt+hmac exactly or has a known + * ssl_peer_key which salt+hmac's to the same. */ + for(i = 0; scache && i < scache->peer_count; i++) { + struct Curl_ssl_scache_peer *peer = &scache->peers[i]; + if(!cf_ssl_scache_match_auth(peer, NULL)) + continue; + if(scache->peers[i].hmac_set && + !memcmp(peer->key_salt, salt, sizeof(peer->key_salt)) && + !memcmp(peer->key_hmac, hmac, sizeof(peer->key_hmac))) { + /* found exact match, return */ + *ppeer = peer; + goto out; + } + else if(peer->ssl_peer_key) { + unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH]; + /* compute hmac for the passed salt */ + result = Curl_hmacit(&Curl_HMAC_SHA256, + salt, sizeof(peer->key_salt), + (const unsigned char *)peer->ssl_peer_key, + strlen(peer->ssl_peer_key), + my_hmac); + if(result) + goto out; + if(!memcmp(my_hmac, hmac, sizeof(my_hmac))) { + /* cryptohash match, take over salt+hmac if no set and return */ + if(!peer->hmac_set) { + memcpy(peer->key_salt, salt, sizeof(peer->key_salt)); + memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac)); + peer->hmac_set = TRUE; + } + *ppeer = peer; + goto out; + } + } + } +out: + return result; +} + +CURLcode Curl_ssl_session_import(struct Curl_easy *data, + const char *ssl_peer_key, + const unsigned char *shmac, size_t shmac_len, + const unsigned char *sdata, size_t sdata_len) +{ + struct Curl_ssl_scache *scache = data->state.ssl_scache; + struct Curl_ssl_scache_peer *peer = NULL; + struct Curl_ssl_session *s = NULL; + bool locked = FALSE; + CURLcode r; + + if(!scache) { + r = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + if(!ssl_peer_key && (!shmac || !shmac_len)) { + r = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + r = Curl_ssl_session_unpack(data, sdata, sdata_len, &s); + if(r) + goto out; + + Curl_ssl_scache_lock(data); + locked = TRUE; + + if(ssl_peer_key) { + r = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer); + if(r) + goto out; + } + else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) { + /* Either salt+hmac was garbled by caller or is from a curl version + * that does things differently */ + r = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + else { + const unsigned char *salt = shmac; + const unsigned char *hmac = shmac + sizeof(peer->key_salt); + + r = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer); + if(r) + goto out; + if(!peer) { + peer = cf_ssl_get_free_peer(scache); + if(peer) { + r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, + NULL, NULL, salt, hmac); + if(r) + goto out; + } + } + } + + if(peer) { + cf_scache_peer_add_session(peer, s, time(NULL)); + s = NULL; /* peer is now owner */ + CURL_TRC_SSLS(data, "successfully imported ticket for peer %s, now " + "with %zu tickets", + peer->ssl_peer_key ? peer->ssl_peer_key : "without key", + Curl_llist_count(&peer->sessions)); + } + +out: + if(locked) + Curl_ssl_scache_unlock(data); + Curl_ssl_session_destroy(s); + return r; +} + +CURLcode Curl_ssl_session_export(struct Curl_easy *data, + curl_ssls_export_cb *export_fn, + void *userptr) +{ + struct Curl_ssl_scache *scache = data->state.ssl_scache; + struct Curl_ssl_scache_peer *peer; + struct dynbuf sbuf, hbuf; + struct Curl_llist_node *n; + size_t i, npeers = 0, ntickets = 0; + curl_off_t now = time(NULL); + CURLcode r = CURLE_OK; + + if(!export_fn) + return CURLE_BAD_FUNCTION_ARGUMENT; + if(!scache) + return CURLE_OK; + + Curl_ssl_scache_lock(data); + + Curl_dyn_init(&hbuf, (CURL_SHA256_DIGEST_LENGTH * 2) + 1); + Curl_dyn_init(&sbuf, CURL_SSL_TICKET_MAX); + + for(i = 0; scache && i < scache->peer_count; i++) { + peer = &scache->peers[i]; + if(!peer->ssl_peer_key && !peer->hmac_set) + continue; /* skip free entry */ + if(peer->clientcert || peer->srp_username || peer->srp_password) + continue; /* not exporting those */ + + Curl_dyn_reset(&hbuf); + cf_scache_peer_remove_expired(peer, now); + n = Curl_llist_head(&peer->sessions); + if(n) + ++npeers; + while(n) { + struct Curl_ssl_session *s = Curl_node_elem(n); + if(!peer->hmac_set) { + r = cf_ssl_scache_peer_set_hmac(peer); + if(r) + goto out; + } + if(!Curl_dyn_len(&hbuf)) { + r = Curl_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt)); + if(r) + goto out; + r = Curl_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac)); + if(r) + goto out; + } + Curl_dyn_reset(&sbuf); + r = Curl_ssl_session_pack(data, s, &sbuf); + if(r) + goto out; + + r = export_fn(data, userptr, peer->ssl_peer_key, + Curl_dyn_uptr(&hbuf), Curl_dyn_len(&hbuf), + Curl_dyn_uptr(&sbuf), Curl_dyn_len(&sbuf), + s->valid_until, s->ietf_tls_id, + s->alpn, s->earlydata_max); + if(r) + goto out; + ++ntickets; + n = Curl_node_next(n); + } + + } + r = CURLE_OK; + CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers", + ntickets, npeers); + +out: + Curl_ssl_scache_unlock(data); + Curl_dyn_free(&hbuf); + Curl_dyn_free(&sbuf); + return r; +} + +#endif /* USE_SSLS_EXPORT */ + +#endif /* USE_SSL */ diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h new file mode 100644 index 0000000..b42873f --- /dev/null +++ b/lib/vtls/vtls_scache.h @@ -0,0 +1,218 @@ +#ifndef HEADER_CURL_VTLS_SCACHE_H +#define HEADER_CURL_VTLS_SCACHE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "cfilters.h" +#include "urldata.h" + +#ifdef USE_SSL + +struct Curl_cfilter; +struct Curl_easy; +struct Curl_ssl_scache; +struct Curl_ssl_session; +struct ssl_peer; + +/* RFC 8446 (TLSv1.3) restrict lifetime to one week max, for + * other, less secure versions, we restrict it to a day */ +#define CURL_SCACHE_MAX_13_LIFETIME_SEC (60*60*24*7) +#define CURL_SCACHE_MAX_12_LIFETIME_SEC (60*60*24) + +/* Create a session cache for up to max_peers endpoints with a total + * of up to max_sessions SSL sessions per peer */ +CURLcode Curl_ssl_scache_create(size_t max_peers, + size_t max_sessions_per_peer, + struct Curl_ssl_scache **pscache); + +void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache); + +/* Create a key from peer and TLS configuration information that is + * unique for how the connection filter wants to establish a TLS + * connection to the peer. + * If the filter is a TLS proxy filter, it will use the proxy relevant + * information. + * @param cf the connection filter wanting to use it + * @param peer the peer the filter wants to talk to + * @param tls_id identifier of TLS implementation for sessions. Should + * include full version if session data from other versions + * is to be avoided. + * @param ppeer_key on successful return, the key generated + */ +CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, + const struct ssl_peer *peer, + const char *tls_id, + char **ppeer_key); + +/* Lock session cache mutex. + * Call this before calling other Curl_ssl_*session* functions + * Caller should unlock this mutex as soon as possible, as it may block + * other SSL connection from making progress. + * The purpose of explicitly locking SSL session cache data is to allow + * individual SSL engines to manage session lifetime in their specific way. + */ +void Curl_ssl_scache_lock(struct Curl_easy *data); + +/* Unlock session cache mutex */ +void Curl_ssl_scache_unlock(struct Curl_easy *data); + +/* Get TLS session object from the cache for the ssl_peer_ey. + * scache mutex must be locked (see Curl_ssl_scache_lock). + * Caller must make sure that the ownership of returned session object + * is properly taken (e.g. its refcount is incremented + * under scache mutex). + * @param cf the connection filter wanting to use it + * @param data the transfer involved + * @param ssl_peer_key the key for lookup + * @param sobj on return, the object for the peer key or NULL + */ +bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + void **sobj); + +typedef void Curl_ssl_scache_obj_dtor(void *sobj); + +/* Add a TLS session related object to the cache. + * Replaces an existing object with the same peer_key. + * scache mutex must be locked (see Curl_ssl_scache_lock). + * Call takes ownership of `sobj`, using `sobj_dtor_cb` + * to deallocate it. Is called in all outcomes, either right away or + * later when the session cache is cleaned up. + * Caller must ensure that it has properly shared ownership of `sobj` + * with cache (e.g. incrementing refcount on success) + * @param cf the connection filter wanting to use it + * @param data the transfer involved + * @param ssl_peer_key the key for lookup + * @param sobj the TLS session object + * @param sobj_free_cb callback to free the session objectt + */ +CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + void *sobj, + Curl_ssl_scache_obj_dtor *sobj_dtor_cb); + +/* All about a SSL session ticket */ +struct Curl_ssl_session { + const unsigned char *sdata; /* session ticket data, plain bytes */ + size_t sdata_len; /* number of bytes in sdata */ + curl_off_t valid_until; /* seconds since EPOCH until ticket expires */ + int ietf_tls_id; /* TLS protocol identifier negotiated */ + char *alpn; /* APLN TLS negotiated protocol string */ + size_t earlydata_max; /* max 0-RTT data supported by peer */ + const unsigned char *quic_tp; /* Optional QUIC transport param bytes */ + size_t quic_tp_len; /* number of bytes in quic_tp */ + struct Curl_llist_node list; /* internal storage handling */ +}; + +/* Create a `session` instance. Does NOT need locking. + * Takes ownership of `sdata` and `sobj` regardless of return code. + * @param sdata bytes of SSL session data or NULL (sobj then required) + * @param sdata_len amount of session data bytes + * @param ietf_tls_id IETF protocol version, e.g. 0x304 for TLSv1.3 + * @param alpn ALPN protocol selected or NULL + * @param valid_until seconds since EPOCH when session expires, pass 0 + * in case this is not known. + * @param psession on return the scached session instance created + */ +CURLcode +Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len, + int ietf_tls_id, const char *alpn, + curl_off_t valid_until, + size_t earlydata_max, + struct Curl_ssl_session **psession); + +/* Variation of session creation with quic transport parameter bytes, + * Takes ownership of `quic_tp` regardless of return code. */ +CURLcode +Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len, + int ietf_tls_id, const char *alpn, + curl_off_t valid_until, + size_t earlydata_max, + unsigned char *quic_tp, size_t quic_tp_len, + struct Curl_ssl_session **psession); + +/* Destroy a `session` instance. Can be called with NULL. + * Does NOT need locking. */ +void Curl_ssl_session_destroy(struct Curl_ssl_session *s); + +/* Put the scache session into the cache. Does NOT need locking. + * Call takes ownership of `s` in all outcomes. + * @param cf the connection filter wanting to use it + * @param data the transfer involved + * @param ssl_peer_key the key for lookup + * @param s the scache session object + */ +CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + struct Curl_ssl_session *s); + +/* Take a matching scache session from the cache. Does NOT need locking. + * @param cf the connection filter wanting to use it + * @param data the transfer involved + * @param ssl_peer_key the key for lookup + * @param s on return, the scache session object or NULL + */ +CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + struct Curl_ssl_session **ps); + +/* Return a taken scache session to the cache. Does NOT need locking. + * Depending on TLS version and other criteria, it may cache it again + * or destroy it. Maybe called with a NULL session. + */ +void Curl_ssl_scache_return(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + struct Curl_ssl_session *s); + +/* Remove all sessions and obj for the peer_key. Does NOT need locking. */ +void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key); + +#ifdef USE_SSLS_EXPORT + +CURLcode Curl_ssl_session_import(struct Curl_easy *data, + const char *ssl_peer_key, + const unsigned char *shmac, size_t shmac_len, + const unsigned char *sdata, size_t sdata_len); + +CURLcode Curl_ssl_session_export(struct Curl_easy *data, + curl_ssls_export_cb *export_fn, + void *userptr); + +#endif /* USE_SSLS_EXPORT */ + +#else /* USE_SSL */ + +#define Curl_ssl_scache_create(x,y,z) ((void)x, CURLE_OK) +#define Curl_ssl_scache_destroy(x) do {} while(0) + +#endif /* USE_SSL (else) */ + +#endif /* HEADER_CURL_VTLS_SCACHE_H */ diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c new file mode 100644 index 0000000..6dec8e5 --- /dev/null +++ b/lib/vtls/vtls_spack.c @@ -0,0 +1,345 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_SSLS_EXPORT + +#include "urldata.h" +#include "curl_trc.h" +#include "vtls_scache.h" +#include "vtls_spack.h" +#include "strdup.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#ifdef _MSC_VER +#if _MSC_VER >= 1600 +#include +#else +typedef unsigned char uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#endif +#endif /* _MSC_VER */ + +#ifndef UINT16_MAX +#define UINT16_MAX 0xffff +#endif +#ifndef UINT32_MAX +#define UINT32_MAX 0xffffffff +#endif + +#define CURL_SPACK_VERSION 0x01 +#define CURL_SPACK_IETF_ID 0x02 +#define CURL_SPACK_VALID_UNTIL 0x03 +#define CURL_SPACK_TICKET 0x04 +#define CURL_SPACK_ALPN 0x05 +#define CURL_SPACK_EARLYDATA 0x06 +#define CURL_SPACK_QUICTP 0x07 + +static CURLcode spack_enc8(struct dynbuf *buf, uint8_t b) +{ + return Curl_dyn_addn(buf, &b, 1); +} + +static CURLcode +spack_dec8(uint8_t *val, const uint8_t **src, const uint8_t *end) +{ + if(end - *src < 1) + return CURLE_READ_ERROR; + *val = **src; + *src += 1; + return CURLE_OK; +} + +static CURLcode spack_enc16(struct dynbuf *buf, uint16_t val) +{ + uint8_t nval[2]; + nval[0] = (uint8_t)(val >> 8); + nval[1] = (uint8_t)val; + return Curl_dyn_addn(buf, nval, sizeof(nval)); +} + +static CURLcode +spack_dec16(uint16_t *val, const uint8_t **src, const uint8_t *end) +{ + if(end - *src < 2) + return CURLE_READ_ERROR; + *val = (uint16_t)((*src)[0] << 8 | (*src)[1]); + *src += 2; + return CURLE_OK; +} + +static CURLcode spack_enc32(struct dynbuf *buf, uint32_t val) +{ + uint8_t nval[4]; + nval[0] = (uint8_t)(val >> 24); + nval[1] = (uint8_t)(val >> 16); + nval[2] = (uint8_t)(val >> 8); + nval[3] = (uint8_t)val; + return Curl_dyn_addn(buf, nval, sizeof(nval)); +} + +static CURLcode +spack_dec32(uint32_t *val, const uint8_t **src, const uint8_t *end) +{ + if(end - *src < 4) + return CURLE_READ_ERROR; + *val = (uint32_t)(*src)[0] << 24 | (uint32_t)(*src)[1] << 16 | + (uint32_t)(*src)[2] << 8 | (*src)[3]; + *src += 4; + return CURLE_OK; +} + +static CURLcode spack_enc64(struct dynbuf *buf, uint64_t val) +{ + uint8_t nval[8]; + nval[0] = (uint8_t)(val >> 56); + nval[1] = (uint8_t)(val >> 48); + nval[2] = (uint8_t)(val >> 40); + nval[3] = (uint8_t)(val >> 32); \ + nval[4] = (uint8_t)(val >> 24); + nval[5] = (uint8_t)(val >> 16); + nval[6] = (uint8_t)(val >> 8); + nval[7] = (uint8_t)val; + return Curl_dyn_addn(buf, nval, sizeof(nval)); +} + +static CURLcode +spack_dec64(uint64_t *val, const uint8_t **src, const uint8_t *end) +{ + if(end - *src < 8) + return CURLE_READ_ERROR; + *val = (uint64_t)(*src)[0] << 56 | (uint64_t)(*src)[1] << 48 | + (uint64_t)(*src)[2] << 40 | (uint64_t)(*src)[3] << 32 | + (uint64_t)(*src)[4] << 24 | (uint64_t)(*src)[5] << 16 | + (uint64_t)(*src)[6] << 8 | (*src)[7]; + *src += 8; + return CURLE_OK; +} + +static CURLcode spack_encstr16(struct dynbuf *buf, const char *s) +{ + size_t slen = strlen(s); + CURLcode r; + if(slen > UINT16_MAX) + return CURLE_BAD_FUNCTION_ARGUMENT; + r = spack_enc16(buf, (uint16_t)slen); + if(!r) { + r = Curl_dyn_addn(buf, s, slen); + } + return r; +} + +static CURLcode +spack_decstr16(char **val, const uint8_t **src, const uint8_t *end) +{ + uint16_t slen; + CURLcode r; + + *val = NULL; + r = spack_dec16(&slen, src, end); + if(r) + return r; + if(end - *src < slen) + return CURLE_READ_ERROR; + *val = Curl_memdup0((const char *)(*src), slen); + *src += slen; + return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} + +static CURLcode spack_encdata16(struct dynbuf *buf, + const uint8_t *data, size_t data_len) +{ + CURLcode r; + if(data_len > UINT16_MAX) + return CURLE_BAD_FUNCTION_ARGUMENT; + r = spack_enc16(buf, (uint16_t)data_len); + if(!r) { + r = Curl_dyn_addn(buf, data, data_len); + } + return r; +} + +static CURLcode +spack_decdata16(uint8_t **val, size_t *val_len, + const uint8_t **src, const uint8_t *end) +{ + uint16_t data_len; + CURLcode r; + + *val = NULL; + r = spack_dec16(&data_len, src, end); + if(r) + return r; + if(end - *src < data_len) + return CURLE_READ_ERROR; + *val = Curl_memdup0((const char *)(*src), data_len); + *val_len = data_len; + *src += data_len; + return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} + +CURLcode Curl_ssl_session_pack(struct Curl_easy *data, + struct Curl_ssl_session *s, + struct dynbuf *buf) +{ + CURLcode r; + DEBUGASSERT(s->sdata); + DEBUGASSERT(s->sdata_len); + + if(s->valid_until < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + + r = spack_enc8(buf, CURL_SPACK_VERSION); + if(!r) + r = spack_enc8(buf, CURL_SPACK_TICKET); + if(!r) + r = spack_encdata16(buf, s->sdata, s->sdata_len); + if(!r) + r = spack_enc8(buf, CURL_SPACK_IETF_ID); + if(!r) + r = spack_enc16(buf, (uint16_t)s->ietf_tls_id); + if(!r) + r = spack_enc8(buf, CURL_SPACK_VALID_UNTIL); + if(!r) + r = spack_enc64(buf, (uint64_t)s->valid_until); + if(!r && s->alpn) { + r = spack_enc8(buf, CURL_SPACK_ALPN); + if(!r) + r = spack_encstr16(buf, s->alpn); + } + if(!r && s->earlydata_max) { + if(s->earlydata_max > UINT32_MAX) + r = CURLE_BAD_FUNCTION_ARGUMENT; + if(!r) + r = spack_enc8(buf, CURL_SPACK_EARLYDATA); + if(!r) + r = spack_enc32(buf, (uint32_t)s->earlydata_max); + } + if(!r && s->quic_tp && s->quic_tp_len) { + r = spack_enc8(buf, CURL_SPACK_QUICTP); + if(!r) + r = spack_encdata16(buf, s->quic_tp, s->quic_tp_len); + } + + if(r) + CURL_TRC_SSLS(data, "error packing data: %d", r); + return r; +} + +CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, + const unsigned char *buf, size_t buflen, + struct Curl_ssl_session **ps) +{ + struct Curl_ssl_session *s = NULL; + const unsigned char *end = buf + buflen; + uint8_t val8, *pval8; + uint16_t val16; + uint32_t val32; + uint64_t val64; + CURLcode r; + + DEBUGASSERT(buf); + DEBUGASSERT(buflen); + *ps = NULL; + + r = spack_dec8(&val8, &buf, end); + if(r) + goto out; + if(val8 != CURL_SPACK_VERSION) { + r = CURLE_READ_ERROR; + goto out; + } + + s = calloc(1, sizeof(*s)); + if(!s) { + r = CURLE_OUT_OF_MEMORY; + goto out; + } + + while(buf < end) { + r = spack_dec8(&val8, &buf, end); + if(r) + goto out; + + switch(val8) { + case CURL_SPACK_ALPN: + r = spack_decstr16(&s->alpn, &buf, end); + if(r) + goto out; + break; + case CURL_SPACK_EARLYDATA: + r = spack_dec32(&val32, &buf, end); + if(r) + goto out; + s->earlydata_max = val32; + break; + case CURL_SPACK_IETF_ID: + r = spack_dec16(&val16, &buf, end); + if(r) + goto out; + s->ietf_tls_id = val16; + break; + case CURL_SPACK_QUICTP: { + r = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); + if(r) + goto out; + s->quic_tp = pval8; + break; + } + case CURL_SPACK_TICKET: { + r = spack_decdata16(&pval8, &s->sdata_len, &buf, end); + if(r) + goto out; + s->sdata = pval8; + break; + } + case CURL_SPACK_VALID_UNTIL: + r = spack_dec64(&val64, &buf, end); + if(r) + goto out; + s->valid_until = (curl_off_t)val64; + break; + default: /* unknown tag */ + r = CURLE_READ_ERROR; + goto out; + } + } + +out: + if(r) { + CURL_TRC_SSLS(data, "error unpacking data: %d", r); + Curl_ssl_session_destroy(s); + } + else + *ps = s; + return r; +} + +#endif /* USE_SSLS_EXPORT */ diff --git a/lib/vtls/vtls_spack.h b/lib/vtls/vtls_spack.h new file mode 100644 index 0000000..8905d7f --- /dev/null +++ b/lib/vtls/vtls_spack.h @@ -0,0 +1,43 @@ +#ifndef HEADER_CURL_VTLS_SPACK_H +#define HEADER_CURL_VTLS_SPACK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_SSLS_EXPORT + +struct dynbuf; +struct Curl_ssl_session; + +CURLcode Curl_ssl_session_pack(struct Curl_easy *data, + struct Curl_ssl_session *s, + struct dynbuf *buf); + +CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, + const unsigned char *buf, size_t buflen, + struct Curl_ssl_session **ps); + +#endif /* USE_SSLS_EXPORT */ + +#endif /* HEADER_CURL_VTLS_SPACK_H */ diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 3394cb2..82b21af 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -60,6 +60,7 @@ #include "inet_pton.h" #include "vtls.h" #include "vtls_int.h" +#include "vtls_scache.h" #include "keylog.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ @@ -69,7 +70,6 @@ #include "curl_printf.h" #include "multiif.h" -#include #include #include #include "wolfssl.h" @@ -78,13 +78,9 @@ #include "curl_memory.h" #include "memdebug.h" -#ifdef USE_ECH -# include "curl_base64.h" -# define ECH_ENABLED(__data__) \ - (__data__->set.tls_ech && \ - !(__data__->set.tls_ech & CURLECH_DISABLE)\ - ) -#endif /* USE_ECH */ +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG +#define USE_ECH_WOLFSSL +#endif /* KEEP_PEER_CERT is a product of the presence of build time symbol OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is @@ -299,7 +295,7 @@ static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) #ifdef WOLFSSL_BIO_CTRL_EOF case WOLFSSL_BIO_CTRL_EOF: /* EOF has been reached on input? */ - return (!cf->next || !cf->next->connected); + return !cf->next || !cf->next->connected; #endif default: ret = 0; @@ -400,57 +396,54 @@ static void wolfssl_bio_cf_free_methods(void) #endif /* !USE_BIO_CHAIN */ -static void wolfssl_session_free(void *sdata, size_t slen) -{ - (void)slen; - free(sdata); -} - -CURLcode wssl_cache_session(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - WOLFSSL_SESSION *session) +CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + WOLFSSL_SESSION *session, + int ietf_tls_id, + const char *alpn) { CURLcode result = CURLE_OK; + struct Curl_ssl_session *sc_session = NULL; unsigned char *sdata = NULL; - unsigned int slen; + unsigned int sdata_len; if(!session) goto out; - slen = wolfSSL_i2d_SSL_SESSION(session, NULL); - if(slen <= 0) { - CURL_TRC_CF(data, cf, "fail to assess session length: %u", slen); + sdata_len = wolfSSL_i2d_SSL_SESSION(session, NULL); + if(sdata_len <= 0) { + CURL_TRC_CF(data, cf, "fail to assess session length: %u", sdata_len); result = CURLE_FAILED_INIT; goto out; } - sdata = calloc(1, slen); + sdata = calloc(1, sdata_len); if(!sdata) { - failf(data, "unable to allocate session buffer of %u bytes", slen); + failf(data, "unable to allocate session buffer of %u bytes", sdata_len); result = CURLE_OUT_OF_MEMORY; goto out; } - slen = wolfSSL_i2d_SSL_SESSION(session, &sdata); - if(slen <= 0) { - CURL_TRC_CF(data, cf, "fail to serialize session: %u", slen); + sdata_len = wolfSSL_i2d_SSL_SESSION(session, &sdata); + if(sdata_len <= 0) { + CURL_TRC_CF(data, cf, "fail to serialize session: %u", sdata_len); result = CURLE_FAILED_INIT; goto out; } - Curl_ssl_sessionid_lock(data); - result = Curl_ssl_set_sessionid(cf, data, peer, NULL, - sdata, slen, wolfssl_session_free); - Curl_ssl_sessionid_unlock(data); - if(result) - failf(data, "failed to add new ssl session to cache (%d)", result); - else { - CURL_TRC_CF(data, cf, "added new session to cache"); - sdata = NULL; + result = Curl_ssl_session_create(sdata, sdata_len, + ietf_tls_id, alpn, + (curl_off_t)time(NULL) + + wolfSSL_SESSION_get_timeout(session), 0, + &sc_session); + sdata = NULL; /* took ownership of sdata */ + if(!result) { + result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session); + /* took ownership of `sc_session` */ } out: free(sdata); - return 0; + return result; } static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) @@ -465,32 +458,35 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) DEBUGASSERT(connssl); DEBUGASSERT(data); if(connssl && data) { - (void)wssl_cache_session(cf, data, &connssl->peer, session); + (void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key, + session, wolfSSL_version(ssl), + connssl->negotiated.alpn); } } return 0; } -CURLcode wssl_setup_session(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct wolfssl_ctx *wss, - struct ssl_peer *peer) +CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wss, + const char *ssl_peer_key) { - void *psdata; - const unsigned char *sdata = NULL; - size_t slen = 0; - CURLcode result = CURLE_OK; + struct Curl_ssl_session *sc_session = NULL; + CURLcode result; - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, peer, &psdata, &slen, NULL)) { + result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &sc_session); + if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) { WOLFSSL_SESSION *session; - sdata = psdata; - session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)slen); + /* wolfSSL changes the passed pointer for whatever reasons, yikes */ + const unsigned char *sdata = sc_session->sdata; + session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, + (long)sc_session->sdata_len); if(session) { int ret = wolfSSL_set_session(wss->handle, session); if(ret != WOLFSSL_SUCCESS) { - Curl_ssl_delsessionid(data, psdata); - infof(data, "previous session not accepted (%d), " + Curl_ssl_session_destroy(sc_session); + sc_session = NULL; + infof(data, "cached session not accepted (%d), " "removing from cache", ret); } else @@ -501,14 +497,14 @@ CURLcode wssl_setup_session(struct Curl_cfilter *cf, failf(data, "could not decode previous session"); } } - Curl_ssl_sessionid_unlock(data); + Curl_ssl_scache_return(cf, data, ssl_peer_key, sc_session); return result; } -static CURLcode populate_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - WOLFSSL_X509_STORE *store, - struct wolfssl_ctx *wssl) +static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + WOLFSSL_X509_STORE *store, + struct wolfssl_ctx *wssl) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; @@ -556,7 +552,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, #ifndef NO_FILESYSTEM /* load trusted cacert from file if not blob */ - CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d", + CURL_TRC_CF(data, cf, "wssl_populate_x509_store, path=%s, blob=%d", ssl_cafile ? ssl_cafile : "none", !!ca_info_blob); if(!store) return CURLE_OUT_OF_MEMORY; @@ -620,8 +616,8 @@ static void wssl_x509_share_free(void *key, size_t key_len, void *p) } static bool -cached_x509_store_expired(const struct Curl_easy *data, - const struct wssl_x509_share *mb) +wssl_cached_x509_store_expired(const struct Curl_easy *data, + const struct wssl_x509_share *mb) { const struct ssl_general_config *cfg = &data->set.general_ssl; struct curltime now = Curl_now(); @@ -635,8 +631,8 @@ cached_x509_store_expired(const struct Curl_easy *data, } static bool -cached_x509_store_different(struct Curl_cfilter *cf, - const struct wssl_x509_share *mb) +wssl_cached_x509_store_different(struct Curl_cfilter *cf, + const struct wssl_x509_share *mb) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); if(!mb->CAfile || !conn_config->CAfile) @@ -645,8 +641,8 @@ cached_x509_store_different(struct Curl_cfilter *cf, return strcmp(mb->CAfile, conn_config->CAfile); } -static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data) +static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) { struct Curl_multi *multi = data->multi; struct wssl_x509_share *share; @@ -657,17 +653,17 @@ static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, (void *)MPROTO_WSSL_X509_KEY, sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL; if(share && share->store && - !cached_x509_store_expired(data, share) && - !cached_x509_store_different(cf, share)) { + !wssl_cached_x509_store_expired(data, share) && + !wssl_cached_x509_store_different(cf, share)) { store = share->store; } return store; } -static void set_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data, - WOLFSSL_X509_STORE *store) +static void wssl_set_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + WOLFSSL_X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; @@ -735,7 +731,8 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, !ssl_config->primary.CRLfile && !ssl_config->native_ca_store; - cached_store = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL; + cached_store = cache_criteria_met ? wssl_get_cached_x509_store(cf, data) + : NULL; if(cached_store && wolfSSL_CTX_get_cert_store(wssl->ctx) == cached_store) { /* The cached store is already in use, do nothing. */ } @@ -752,15 +749,15 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, } wolfSSL_CTX_set_cert_store(wssl->ctx, store); - result = populate_x509_store(cf, data, store, wssl); + result = wssl_populate_x509_store(cf, data, store, wssl); if(!result) { - set_cached_x509_store(cf, data, store); + wssl_set_cached_x509_store(cf, data, store); } } else { /* We never share the CTX's store, use it. */ WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); - result = populate_x509_store(cf, data, store, wssl); + result = wssl_populate_x509_store(cf, data, store, wssl); } return result; @@ -1192,13 +1189,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Check if there is a cached ID we can/should use here! */ if(ssl_config->primary.cache_session) { /* Set session from cache if there is one */ - (void)wssl_setup_session(cf, data, backend, &connssl->peer); + (void)Curl_wssl_setup_session(cf, data, backend, connssl->peer.scache_key); /* Register to get notified when a new session is received */ wolfSSL_set_app_data(backend->handle, cf); wolfSSL_CTX_sess_set_new_cb(backend->ctx, wssl_vtls_new_session_cb); } -#ifdef USE_ECH +#ifdef USE_ECH_WOLFSSL if(ECH_ENABLED(data)) { int trying_ech_now = 0; @@ -1265,14 +1262,14 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } - if(trying_ech_now - && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) { + if(trying_ech_now && wolfSSL_set_min_proto_version(backend->handle, + TLS1_3_VERSION) != 1) { infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); return CURLE_SSL_CONNECT_ERROR; } } -#endif /* USE_ECH */ +#endif /* USE_ECH_WOLFSSL */ #ifdef USE_BIO_CHAIN { @@ -1441,7 +1438,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "server verification failed: certificate not valid yet."); return CURLE_PEER_FAILED_VERIFICATION; } -#ifdef USE_ECH +#ifdef USE_ECH_WOLFSSL else if(-1 == detail) { /* try access a retry_config ECHConfigList for tracing */ byte echConfigs[1000]; @@ -1796,7 +1793,7 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, } -static size_t wolfssl_version(char *buffer, size_t size) +size_t Curl_wssl_version(char *buffer, size_t size) { #if LIBWOLFSSL_VERSION_HEX >= 0x03006000 return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version()); @@ -2020,7 +2017,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #endif SSLSUPP_CA_PATH | SSLSUPP_CAINFO_BLOB | -#ifdef USE_ECH +#ifdef USE_ECH_WOLFSSL SSLSUPP_ECH | #endif SSLSUPP_SSL_CTX | @@ -2034,25 +2031,22 @@ const struct Curl_ssl Curl_ssl_wolfssl = { wolfssl_init, /* init */ wolfssl_cleanup, /* cleanup */ - wolfssl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ + Curl_wssl_version, /* version */ wolfssl_shutdown, /* shutdown */ wolfssl_data_pending, /* data_pending */ wolfssl_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ + NULL, /* cert_status_request */ wolfssl_connect, /* connect */ wolfssl_connect_nonblocking, /* connect_nonblocking */ Curl_ssl_adjust_pollset, /* adjust_pollset */ wolfssl_get_internals, /* get_internals */ wolfssl_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ + NULL, /* close_all */ + NULL, /* set_engine */ + NULL, /* set_engine_default */ + NULL, /* engines_list */ + NULL, /* false_start */ wolfssl_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ wolfssl_recv, /* recv decrypted data */ wolfssl_send, /* send data to encrypt */ NULL, /* get_channel_binding */ diff --git a/lib/vtls/wolfssl.h b/lib/vtls/wolfssl.h index dc2967d..57935b6 100644 --- a/lib/vtls/wolfssl.h +++ b/lib/vtls/wolfssl.h @@ -47,19 +47,23 @@ struct wolfssl_ctx { BIT(shutting_down); /* TLS is being shut down */ }; +size_t Curl_wssl_version(char *buffer, size_t size); + CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, struct wolfssl_ctx *wssl); -CURLcode wssl_setup_session(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct wolfssl_ctx *wss, - struct ssl_peer *peer); +CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wss, + const char *ssl_peer_key); -CURLcode wssl_cache_session(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - WOLFSSL_SESSION *session); +CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *ssl_peer_key, + WOLFSSL_SESSION *session, + int ietf_tls_id, + const char *alpn); #endif /* USE_WOLFSSL */ diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c index fe4a38b..0bc0a75 100644 --- a/lib/vtls/x509asn1.c +++ b/lib/vtls/x509asn1.c @@ -178,8 +178,11 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, const char *beg, const char *end) WARN_UNUSED_RESULT; -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) +#define CURL_ASN1_MAX_RECURSIONS 16 + +static const char *getASN1Element_(struct Curl_asn1Element *elem, + const char *beg, const char *end, + size_t lvl) { unsigned char b; size_t len; @@ -190,7 +193,8 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, Returns a pointer in source string after the parsed element, or NULL if an error occurs. */ if(!beg || !end || beg >= end || !*beg || - (size_t)(end - beg) > CURL_ASN1_MAX) + ((size_t)(end - beg) > CURL_ASN1_MAX) || + lvl >= CURL_ASN1_MAX_RECURSIONS) return NULL; /* Process header byte. */ @@ -216,7 +220,7 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, return NULL; elem->beg = beg; while(beg < end && *beg) { - beg = getASN1Element(&lelem, beg, end); + beg = getASN1Element_(&lelem, beg, end, lvl + 1); if(!beg) return NULL; } @@ -243,6 +247,12 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, return elem->end; } +static const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) +{ + return getASN1Element_(elem, beg, end, 0); +} + #ifdef WANT_EXTRACT_CERTINFO /* @@ -259,6 +269,17 @@ static const struct Curl_OID *searchOID(const char *oid) return NULL; } +#ifdef UNITTESTS +/* used by unit1657.c */ +CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) +{ + if(getASN1Element(elem, beg, end)) + return CURLE_OK; + return CURLE_BAD_FUNCTION_ARGUMENT; +} +#endif + /* * Convert an ASN.1 Boolean value into its string representation. * diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h index 5b48596..5de8f18 100644 --- a/lib/vtls/x509asn1.h +++ b/lib/vtls/x509asn1.h @@ -85,6 +85,9 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, /* used by unit1656.c */ CURLcode Curl_x509_GTime2str(struct dynbuf *store, const char *beg, const char *end); +/* used by unit1657.c */ +CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end); #endif #endif diff --git a/lib/warnless.c b/lib/warnless.c index 8da3be3..4169960 100644 --- a/lib/warnless.c +++ b/lib/warnless.c @@ -101,28 +101,6 @@ unsigned char curlx_ultouc(unsigned long ulnum) } /* -** unsigned size_t to signed curl_off_t -*/ - -curl_off_t curlx_uztoso(size_t uznum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable:4310) /* cast truncates constant value */ -#endif - - DEBUGASSERT(uznum <= (size_t) CURL_MASK_SCOFFT); - return (curl_off_t)(uznum & (size_t) CURL_MASK_SCOFFT); - -#if defined(__INTEL_COMPILER) || defined(_MSC_VER) -# pragma warning(pop) -#endif -} - -/* ** unsigned size_t to signed int */ @@ -148,8 +126,8 @@ int curlx_uztosi(size_t uznum) unsigned long curlx_uztoul(size_t uznum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ #endif #if ULONG_MAX < SIZE_T_MAX @@ -158,7 +136,7 @@ unsigned long curlx_uztoul(size_t uznum) return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG); #ifdef __INTEL_COMPILER -# pragma warning(pop) +# pragma warning(pop) #endif } @@ -169,8 +147,8 @@ unsigned long curlx_uztoul(size_t uznum) unsigned int curlx_uztoui(size_t uznum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +# pragma warning(push) +# pragma warning(disable:810) /* conversion may lose significant bits */ #endif #if UINT_MAX < SIZE_T_MAX @@ -179,7 +157,7 @@ unsigned int curlx_uztoui(size_t uznum) return (unsigned int)(uznum & (size_t) CURL_MASK_UINT); #ifdef __INTEL_COMPILER -# pragma warning(pop) +# pragma warning(pop) #endif } diff --git a/lib/warnless.h b/lib/warnless.h index 972c7a9..fe34532 100644 --- a/lib/warnless.h +++ b/lib/warnless.h @@ -39,8 +39,6 @@ unsigned char curlx_ultouc(unsigned long ulnum); int curlx_uztosi(size_t uznum); -curl_off_t curlx_uztoso(size_t uznum); - unsigned long curlx_uztoul(size_t uznum); unsigned int curlx_uztoui(size_t uznum); diff --git a/lib/ws.c b/lib/ws.c index 3d739a5..25d19c6 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -47,6 +47,10 @@ #define WSBIT_FIN 0x80 +#define WSBIT_RSV1 0x40 +#define WSBIT_RSV2 0x20 +#define WSBIT_RSV3 0x10 +#define WSBIT_RSV_MASK (WSBIT_RSV1 | WSBIT_RSV2 | WSBIT_RSV3) #define WSBIT_OPCODE_CONT 0 #define WSBIT_OPCODE_TEXT (1) #define WSBIT_OPCODE_BIN (2) @@ -108,6 +112,13 @@ static unsigned char ws_frame_flags2op(int flags) return 0; } +/* No extensions are supported. If any of the RSV bits are set, we must fail */ +static bool ws_frame_rsv_supported(int flags) +{ + unsigned char reserved_bits = flags & WSBIT_RSV_MASK; + return reserved_bits == 0; +} + static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, const char *msg) { @@ -175,9 +186,17 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, dec->head[0] = *inbuf; Curl_bufq_skip(inraw, 1); + if(!ws_frame_rsv_supported(dec->head[0])) { + failf(data, "WS: unknown reserved bit in frame header: %x", + dec->head[0] & WSBIT_RSV_MASK); + ws_dec_reset(dec); + return CURLE_RECV_ERROR; + } + dec->frame_flags = ws_frame_op2flags(dec->head[0]); if(!dec->frame_flags) { - failf(data, "WS: unknown opcode: %x", dec->head[0]); + failf(data, "WS: unknown opcode: %x", + dec->head[0] & WSBIT_OPCODE_MASK); ws_dec_reset(dec); return CURLE_RECV_ERROR; } @@ -676,7 +695,7 @@ struct wsfield { const char *val; }; -CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req) +CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req) { unsigned int i; CURLcode result = CURLE_OK; @@ -729,16 +748,8 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req) free(randstr); for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) { if(!Curl_checkheaders(data, STRCONST(heads[i].name))) { -#ifdef USE_HYPER - char field[128]; - msnprintf(field, sizeof(field), "%s %s", heads[i].name, - heads[i].val); - result = Curl_hyper_header(data, req, field); -#else - (void)data; result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name, heads[i].val); -#endif } } k->upgr101 = UPGR101_WS; @@ -1017,8 +1028,28 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, CURLcode result; const unsigned char *out; size_t outlen, n; +#ifdef DEBUGBUILD + /* Simulate a blocking send after this chunk has been sent */ + bool eagain_next = FALSE; + size_t chunk_egain = 0; + char *p = getenv("CURL_WS_CHUNK_EAGAIN"); + if(p) { + long l = strtol(p, NULL, 10); + if(l > 0 && l <= (1*1024*1024)) { + chunk_egain = (size_t)l; + } + } +#endif while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) { +#ifdef DEBUGBUILD + if(eagain_next) + return CURLE_AGAIN; + if(chunk_egain && (outlen > chunk_egain)) { + outlen = chunk_egain; + eagain_next = TRUE; + } +#endif if(blocking) { result = ws_send_raw_blocking(data, ws, (char *)out, outlen); n = result ? 0 : outlen; @@ -1127,15 +1158,15 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, return result; } -CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer, +CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags) { struct websocket *ws; + const unsigned char *buffer = buffer_arg; ssize_t n; - size_t space, payload_added; - CURLcode result; + CURLcode result = CURLE_OK; struct Curl_easy *data = d; CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T @@ -1159,13 +1190,13 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer, } 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 */ + /* try flushing any content still waiting to be sent. */ + result = ws_flush(data, ws, FALSE); + if(result) + goto out; + if(fragsize || flags) { failf(data, "ws_send, raw mode: fragsize and flags cannot be non-zero"); return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1175,87 +1206,87 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer, } /* Not RAW mode, buf we do the frame encoding */ - space = Curl_bufq_space(&ws->sendbuf); - 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) { - /* a frame series 'fragsize' bytes big, this is the first */ - n = ws_enc_write_head(data, &ws->enc, flags, fragsize, - &ws->sendbuf, &result); - if(n < 0) - goto out; + if(ws->enc.payload_remain || !Curl_bufq_is_empty(&ws->sendbuf)) { + /* a frame is ongoing with payload buffered or more payload + * that needs to be encoded into the buffer */ + if(buflen < ws->sendbuf_payload) { + /* We have been called with LESS buffer data than before. This + * is not how it's supposed too work. */ + failf(data, "curl_ws_send() called with smaller 'buflen' than " + "bytes already buffered in previous call, %zu vs %zu", + buflen, ws->sendbuf_payload); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } - else { - if((curl_off_t)buflen > ws->enc.payload_remain) { - infof(data, "WS: unaligned frame size (sending %zu instead of %" - FMT_OFF_T ")", - buflen, ws->enc.payload_remain); - } + if((curl_off_t)buflen > + (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) { + /* too large buflen beyond payload length of frame */ + infof(data, "WS: unaligned frame size (sending %zu instead of %" + FMT_OFF_T ")", + buflen, ws->enc.payload_remain + ws->sendbuf_payload); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } } - else if(!ws->enc.payload_remain) { - n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen, + else { + /* starting a new frame, we want a clean sendbuf */ + curl_off_t payload_len = (flags & CURLWS_OFFSET) ? + fragsize : (curl_off_t)buflen; + result = ws_flush(data, ws, Curl_is_in_callback(data)); + if(result) + goto out; + + n = ws_enc_write_head(data, &ws->enc, flags, payload_len, &ws->sendbuf, &result); if(n < 0) goto out; } - n = ws_enc_write_payload(&ws->enc, data, - buffer, buflen, &ws->sendbuf, &result); - if(n < 0) - goto out; - payload_added = (size_t)n; + /* While there is either sendbuf to flush OR more payload to encode... */ + while(!Curl_bufq_is_empty(&ws->sendbuf) || (buflen > ws->sendbuf_payload)) { + /* Try to add more payload to sendbuf */ + if(buflen > ws->sendbuf_payload) { + size_t prev_len = Curl_bufq_len(&ws->sendbuf); + n = ws_enc_write_payload(&ws->enc, data, + buffer + ws->sendbuf_payload, + buflen - ws->sendbuf_payload, + &ws->sendbuf, &result); + if(n < 0 && (result != CURLE_AGAIN)) + goto out; + ws->sendbuf_payload += Curl_bufq_len(&ws->sendbuf) - prev_len; + } - 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); - } + *sent += ws->sendbuf_payload; + buffer += ws->sendbuf_payload; + buflen -= ws->sendbuf_payload; + ws->sendbuf_payload = 0; } 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; + if(ws->sendbuf_payload > Curl_bufq_len(&ws->sendbuf)) { + /* blocked, part of payload bytes remain, report length + * that we managed to send. */ + size_t flushed = (ws->sendbuf_payload - Curl_bufq_len(&ws->sendbuf)); + *sent += flushed; + ws->sendbuf_payload -= flushed; 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); + else { + /* blocked before sending headers or 1st payload byte. We cannot report + * OK on 0-length send (caller counts only payload) and EAGAIN */ + CURL_TRC_WS(data, "EAGAIN flushing sendbuf, payload_encoded: %zu/%zu", + ws->sendbuf_payload, buflen); + DEBUGASSERT(*sent == 0); + result = CURLE_AGAIN; goto out; } - /* We added the complete data to our sendbuf. Report one byte less as - * sent. This partial 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; } + else + goto out; /* real error sending the data */ } out: @@ -1322,6 +1353,7 @@ const struct Curl_handler Curl_handler_ws = { Curl_http_write_resp_hd, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ PORT_HTTP, /* defport */ CURLPROTO_WS, /* protocol */ CURLPROTO_HTTP, /* family */ @@ -1348,6 +1380,7 @@ const struct Curl_handler Curl_handler_wss = { Curl_http_write_resp_hd, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ PORT_HTTPS, /* defport */ CURLPROTO_WSS, /* protocol */ CURLPROTO_HTTP, /* family */ diff --git a/lib/ws.h b/lib/ws.h index 186cc2c..c96bcca 100644 --- a/lib/ws.h +++ b/lib/ws.h @@ -27,12 +27,6 @@ #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) -#ifdef USE_HYPER -#define REQTYPE void -#else -#define REQTYPE struct dynbuf -#endif - /* a client-side WS frame decoder, parsing frame headers and * payload, keeping track of current position and stats */ enum ws_dec_state { @@ -59,7 +53,7 @@ struct ws_encoder { unsigned int xori; /* xor index */ unsigned char mask[4]; /* 32-bit mask for this connection */ unsigned char firstbyte; /* first byte of frame we encode */ - bool contfragment; /* set TRUE if the previous fragment sent was not final */ + BIT(contfragment); /* set TRUE if the previous fragment sent was not final */ }; /* A websocket connection with en- and decoder that treat frames @@ -71,9 +65,10 @@ struct websocket { struct bufq recvbuf; /* raw data from the server */ struct bufq sendbuf; /* raw data to be sent to the server */ struct curl_ws_frame frame; /* the current WS FRAME received */ + size_t sendbuf_payload; /* number of payload bytes in sendbuf */ }; -CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); +CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req); CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len); extern const struct Curl_handler Curl_handler_ws; -- cgit v0.12 From ae94c2369d008277887402f525e3b2667768751c Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 6 Feb 2025 06:37:53 -0500 Subject: curl: Clarify comments marking our local changes to the CMake code --- Utilities/cmcurl/CMakeLists.txt | 62 +++++++++++++++++++------------------ Utilities/cmcurl/lib/CMakeLists.txt | 3 +- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index bc68953..bd464c5 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -215,7 +215,7 @@ endif() # to ON or OFF), the symbol detection is skipped. If the variable is # NOT DEFINED, the symbol detection is performed. -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR) message(STATUS "Using CMake version ${CMAKE_VERSION}") @@ -238,7 +238,7 @@ if(NOT "$ENV{CURL_BUILDINFO}$ENV{CURL_CI}$ENV{CI}" STREQUAL "") endif() endforeach() endif() -endif() +endif() # XXX(cmake): end set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) @@ -253,10 +253,11 @@ string(REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+" _curl_version_nu string(REGEX REPLACE "[^0]+0x" "" _curl_version_num ${_curl_version_num}) unset(_curl_version_h_contents) -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake message(STATUS "curl version=[${_curl_version}]") -endif() -# XXX(CMake): Set these as normal variables to suppress cache entries. +endif() # XXX(cmake): end + +# XXX(cmake): Set these as normal variables to suppress cache entries. set(CMAKE_PROJECT_VERSION 0) set(CMAKE_PROJECT_VERSION_MAJOR 0) set(CMAKE_PROJECT_VERSION_MINOR 0) @@ -331,9 +332,9 @@ endif() if(CMAKE_CROSSCOMPILING) set(_target_flags "${_target_flags} CROSS") endif() -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake message(STATUS "CMake platform flags:${_target_flags}") -endif() +endif() # XXX(cmake): end if(CMAKE_CROSSCOMPILING) message(STATUS "Cross-compiling: " @@ -392,7 +393,7 @@ if(WIN32) endif() endif() - if(0) # This code not needed for building within CMake. + if(0) # XXX(cmake): not needed for build within cmake list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") # Apply to all feature checks set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string") @@ -400,7 +401,7 @@ if(WIN32) add_definitions("-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") # Apply to all feature checks endif() - endif() + endif() # XXX(cmake): end # Detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT curl_internal_test(HAVE_WIN32_WINNT) @@ -429,7 +430,7 @@ elseif(DOS OR AMIGA) endif() option(CURL_LTO "Enable compiler Link Time Optimizations" OFF) -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake if(NOT DOS AND NOT AMIGA) # if c-ares is used, default the threaded resolver to OFF if(ENABLE_ARES) @@ -441,7 +442,7 @@ if(NOT DOS AND NOT AMIGA) endif() include(PickyWarnings) -endif() +endif() # XXX(cmake): end if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE") # Required for sendmmsg() @@ -465,7 +466,7 @@ if(ENABLE_CURLDEBUG) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURLDEBUG") endif() -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF) if(CURL_CLANG_TIDY) set(CMAKE_UNITY_BUILD OFF) @@ -489,7 +490,7 @@ endif() if(NOT DEFINED CMAKE_DEBUG_POSTFIX) set(CMAKE_DEBUG_POSTFIX "-d") endif() -endif() +endif() # XXX(cmake): end set(LIB_STATIC "libcurl_static") set(LIB_SHARED "libcurl_shared") @@ -544,9 +545,9 @@ if(ENABLE_ARES) endif() endif() -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake include(CurlSymbolHiding) -endif() +endif() # XXX(cmake): end option(CURL_ENABLE_EXPORT_TARGET "Enable CMake export target" ON) mark_as_advanced(CURL_ENABLE_EXPORT_TARGET) @@ -599,12 +600,12 @@ option(CURL_DISABLE_LIBCURL_OPTION "Disable --libcurl option from the curl tool" mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION) option(CURL_DISABLE_MIME "Disable MIME support" OFF) mark_as_advanced(CURL_DISABLE_MIME) -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake cmake_dependent_option(CURL_DISABLE_FORM_API "Disable form-api" OFF "NOT CURL_DISABLE_MIME" ON) mark_as_advanced(CURL_DISABLE_FORM_API) -endif() +endif() # XXX(cmake): end option(CURL_DISABLE_MQTT "Disable MQTT" OFF) mark_as_advanced(CURL_DISABLE_MQTT) option(CURL_DISABLE_BINDLOCAL "Disable local binding support" OFF) @@ -706,7 +707,7 @@ if(ENABLE_IPV6) set(USE_IPV6 ON) endif() -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake find_package(Perl) if(PERL_EXECUTABLE) @@ -734,7 +735,7 @@ if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) message(WARNING "Perl not found. Will not build manuals.") endif() endif() -endif() +endif() # XXX(cmake): end # Disable warnings on Borland to avoid changing 3rd party code. if(BORLAND) @@ -823,7 +824,7 @@ if(WIN32) list(APPEND CURL_LIBS "ws2_32" "bcrypt") endif() -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake # Check SSL libraries option(CURL_ENABLE_SSL "Enable SSL support" ON) @@ -863,7 +864,7 @@ if(USE_OPENSSL_QUIC AND NOT CURL_USE_OPENSSL) set(USE_OPENSSL_QUIC OFF) endif() option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF) -endif() +endif() # XXX(cmake): end curl_count_true(_enabled_ssl_options_count CURL_USE_SCHANNEL @@ -911,9 +912,9 @@ if(CURL_USE_SECTRANSP) set(_valid_default_ssl_backend TRUE) endif() - if(0) # This code not needed for building within CMake. + if(0) # XXX(cmake): not needed for build within cmake message(WARNING "Secure Transport does not support TLS 1.3.") - endif() + endif() # XXX(cmake): end endif() if(_use_core_foundation_and_core_services) @@ -1027,9 +1028,9 @@ if(CURL_USE_BEARSSL) endif() set(_curl_ca_bundle_supported TRUE) - if(0) # This code not needed for building within CMake. + if(0) # XXX(cmake): not needed for build within cmake message(WARNING "BearSSL does not support TLS 1.3.") - endif() + endif() # XXX(cmake): end endif() if(CURL_USE_WOLFSSL) @@ -1688,7 +1689,7 @@ else() unset(USE_UNIX_SOCKETS CACHE) endif() -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake # # CA handling # @@ -1777,7 +1778,7 @@ if(_curl_ca_bundle_supported) endif() endif() endif() -endif() +endif() # XXX(cmake): end if(WIN32) option(CURL_DISABLE_CA_SEARCH "Disable unsafe CA bundle search in PATH on Windows" OFF) @@ -2048,7 +2049,7 @@ check_type_size("curl_socket_t" SIZEOF_CURL_SOCKET_T) cmake_pop_check_state() # pop curl system headers cmake_pop_check_state() # pop -D_FILE_OFFSET_BITS=64 -if(0) # This code not needed for building within CMake. +if(0) # XXX(cmake): not needed for build within cmake if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) # On non-Windows and not cross-compiling, check for writable argv[] include(CheckCSourceRuns) @@ -2060,7 +2061,7 @@ if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) return (argv[0][0] == ' ')?0:1; }" HAVE_WRITABLE_ARGV) endif() -endif() +endif() # XXX(cmake): end if(NOT CMAKE_CROSSCOMPILING) include(CheckCSourceRuns) @@ -2221,7 +2222,7 @@ function(curl_transform_makefile_inc _input_file _output_file) endfunction() #----------------------------------------------------------------------------- -# CMake-specific curl code. +# XXX(cmake): begin cmake-specific curl code add_subdirectory(lib) add_executable(curltest curltest.c) @@ -2234,6 +2235,7 @@ endif() install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmcurl) return() # The rest of this file is not needed for building within CMake. +# XXX(cmake): end cmake-specific curl code #----------------------------------------------------------------------------- include(GNUInstallDirs) diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 12b37cd..ecdfe3b 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -47,7 +47,7 @@ if(USE_ARES) endif() #----------------------------------------------------------------------------- -# CMake-specific curl code. +# XXX(cmake): begin cmake-specific curl code unset(LIBCURL_OUTPUT_NAME CACHE) add_library(cmcurl ${HHEADERS} ${CSOURCES}) @@ -82,6 +82,7 @@ if(CURL_USE_OPENSSL AND OPENSSL_FOUND AND WIN32) endif() return() # The rest of this file is not needed for building within CMake. +# XXX(cmake): end cmake-specific curl code #----------------------------------------------------------------------------- if(CURL_BUILD_TESTING) -- cgit v0.12 From 0e1f3f175cf548ae091c9e73da52dab9a898b6fa Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 5 Feb 2025 09:32:36 -0500 Subject: curl: Set build options the way we need for CMake Set options added by the update to curl 8.12.0. --- Utilities/cmcurl/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index bd464c5..4d4166a 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -22,7 +22,7 @@ set(CURL_USE_SCHANNEL OFF) set(CURL_USE_SECTRANSP OFF) set(CURL_USE_WOLFSSH OFF) set(CURL_USE_WOLFSSL OFF) -set(CURL_BROTLI OFF) +set(CURL_BROTLI "OFF" CACHE INTERNAL "Build curl with BROTLI support (AUTO, ON or OFF)") set(CURL_DISABLE_ALTSVC ON) set(CURL_DISABLE_AWS OFF) set(CURL_DISABLE_BASIC_AUTH OFF) @@ -76,7 +76,7 @@ set(CURL_LTO OFF CACHE INTERNAL "Turn on compiler Link Time Optimizations") set(CURL_STATIC_CRT OFF CACHE INTERNAL "Set to ON to build libcurl with static CRT on Windows (/MT).") set(CURL_WERROR OFF CACHE INTERNAL "Turn compiler warnings into errors") set(CURL_ZLIB "AUTO" CACHE INTERNAL "Build curl with ZLIB support (AUTO, ON or OFF)") -set(CURL_ZSTD OFF) +set(CURL_ZSTD "OFF" CACHE INTERNAL "Build curl with ZSTD support (AUTO, ON or OFF)") set(DISABLED_THREADSAFE OFF CACHE INTERNAL "Curl can use thread-safe functions") set(ENABLE_ARES OFF CACHE INTERNAL "No curl c-ares support") set(ENABLE_ALT_SVC OFF) @@ -108,6 +108,7 @@ set(USE_NGHTTP2 ON) set(USE_NGTCP2 OFF) set(USE_OPENSSL_QUIC OFF) set(USE_QUICHE OFF) +set(USE_SSLS_EXPORT OFF) set(USE_WIN32_IDN OFF) set(USE_WIN32_LDAP OFF CACHE INTERNAL "No curl Windows LDAP") if(CURL_USE_OPENSSL) @@ -991,9 +992,11 @@ if(CURL_USE_OPENSSL) set(_openssl "AmiSSL") else() set(_openssl "OpenSSL") + if(0) # XXX(cmake): not needed for build within cmake if(OPENSSL_VERSION VERSION_LESS 1.1.1) message(WARNING "OpenSSL ${OPENSSL_VERSION} does not support TLS 1.3.") endif() + endif() # XXX(cmake): end endif() endif() -- cgit v0.12