diff options
author | Brad King <brad.king@kitware.com> | 2023-03-20 20:40:49 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2023-03-20 20:40:49 (GMT) |
commit | e3dc4df9b95272037d27be25ae5ef030ffd83fa4 (patch) | |
tree | 6132bc02d5204bd988b32ee8ab06053ec7c0e4c4 /Utilities/cmcurl | |
parent | 4e6c3cd93bc870f44442e6d7e59896bc6d86ce25 (diff) | |
parent | e8bff971d9d835a65bcb1711e7214afe37b238e0 (diff) | |
download | CMake-e3dc4df9b95272037d27be25ae5ef030ffd83fa4.zip CMake-e3dc4df9b95272037d27be25ae5ef030ffd83fa4.tar.gz CMake-e3dc4df9b95272037d27be25ae5ef030ffd83fa4.tar.bz2 |
Merge branch 'upstream-curl' into update-curl
* upstream-curl:
curl 2023-03-20 (b16d1fa8)
Diffstat (limited to 'Utilities/cmcurl')
76 files changed, 1934 insertions, 1538 deletions
diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake index 9f4e9f2..ff0d49e 100644 --- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake @@ -71,7 +71,7 @@ endif() if(NGTCP2_FIND_COMPONENTS) set(NGTCP2_CRYPTO_BACKEND "") foreach(component IN LISTS NGTCP2_FIND_COMPONENTS) - if(component MATCHES "^(BoringSSL|OpenSSL|GnuTLS)") + if(component MATCHES "^(BoringSSL|OpenSSL|wolfSSL|GnuTLS)") if(NGTCP2_CRYPTO_BACKEND) message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected") endif() diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake index cef31b5..3771237 100644 --- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake +++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake @@ -29,7 +29,6 @@ if(NOT UNIX) set(HAVE_ARPA_INET_H 0) set(HAVE_FCNTL_H 1) - set(HAVE_INTTYPES_H 0) set(HAVE_IO_H 1) set(HAVE_NETDB_H 0) set(HAVE_NETINET_IN_H 0) @@ -37,7 +36,6 @@ if(NOT UNIX) set(HAVE_PWD_H 0) set(HAVE_SETJMP_H 1) set(HAVE_SIGNAL_H 1) - set(HAVE_STDINT_H 0) set(HAVE_STDLIB_H 1) set(HAVE_STRINGS_H 0) set(HAVE_STRING_H 1) diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index 6fc316d..f10a47d 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -197,7 +197,7 @@ endif() # HAVE_RAND_EGD: `RAND_egd` present in OpenSSL # HAVE_BORINGSSL: OpenSSL is BoringSSL # HAVE_PK11_CREATEMANAGEDGENERICOBJECTL: `PK11_CreateManagedGenericObject` present in NSS -# HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL +# HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL # HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE # HAVE_ZSTD_CREATEDSTREAM: `ZSTD_createDStream` present in Zstd # @@ -523,7 +523,7 @@ if(APPLE) endif() if(WIN32) cmake_dependent_option(CURL_USE_SCHANNEL "enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF) - cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without openssl" ON + cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without OpenSSL" ON CURL_USE_SCHANNEL OFF) endif() cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) @@ -583,6 +583,58 @@ if(use_core_foundation) list(APPEND CURL_LIBS "-framework CoreFoundation") endif() +# Keep compression lib detection before TLS detection, which +# might depend on it. + +set(HAVE_LIBZ OFF) +set(USE_ZLIB OFF) +optional_dependency(ZLIB) +if(ZLIB_FOUND) + set(HAVE_LIBZ ON) + set(USE_ZLIB ON) + + # Depend on ZLIB via imported targets if supported by the running + # version of CMake. This allows our dependents to get our dependencies + # transitively. + if(NOT CMAKE_VERSION VERSION_LESS 3.4) + list(APPEND CURL_LIBS ZLIB::ZLIB) + else() + list(APPEND CURL_LIBS ${ZLIB_LIBRARIES}) + include_directories(${ZLIB_INCLUDE_DIRS}) + endif() + list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS}) +endif() + +option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF) +set(HAVE_BROTLI OFF) +if(CURL_BROTLI) + find_package(Brotli QUIET) + if(BROTLI_FOUND) + set(HAVE_BROTLI ON) + list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) + include_directories(${BROTLI_INCLUDE_DIRS}) + list(APPEND CMAKE_REQUIRED_INCLUDES ${BROTLI_INCLUDE_DIRS}) + endif() +endif() + +option(CURL_ZSTD "Set to ON to enable building curl with zstd support." OFF) +set(HAVE_ZSTD OFF) +if(CURL_ZSTD) + find_package(Zstd REQUIRED) + if (NOT DEFINED HAVE_ZSTD_CREATEDSTREAM) + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES ${Zstd_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LIBRARIES ${Zstd_LIBRARIES}) + check_symbol_exists(ZSTD_createDStream "zstd.h" HAVE_ZSTD_CREATEDSTREAM) + cmake_pop_check_state() + endif() + if(Zstd_FOUND AND HAVE_ZSTD_CREATEDSTREAM) + set(HAVE_ZSTD ON) + list(APPEND CURL_LIBS ${Zstd_LIBRARIES}) + include_directories(${Zstd_INCLUDE_DIRS}) + endif() +endif() + if(CURL_USE_OPENSSL) find_package(OpenSSL) if(NOT OpenSSL_FOUND) @@ -660,23 +712,39 @@ if(USE_NGHTTP2) endif() function(CheckQuicSupportInOpenSSL) - # Be sure that the OpenSSL library actually supports QUIC. + # Be sure that the OpenSSL/wolfSSL library actually supports QUIC. if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD) cmake_push_check_state() - set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") - check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + if(USE_WOLFSSL) + set(CMAKE_REQUIRED_INCLUDES "${WolfSSL_INCLUDE_DIRS}") + set(CMAKE_REQUIRED_LIBRARIES "${WolfSSL_LIBRARIES}") + if(HAVE_LIBZ) + list(APPEND CMAKE_REQUIRED_INCLUDES "${ZLIB_INCLUDE_DIRS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") + endif() + if(WIN32) + list(APPEND CMAKE_REQUIRED_LIBRARIES "crypt32") + endif() + list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T) # to pull in stdint.h (as of wolfSSL v5.5.4) + check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + else() + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") + check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + endif() cmake_pop_check_state() endif() if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) - message(FATAL_ERROR "QUIC support is missing in OpenSSL/BoringSSL. Try setting -DOPENSSL_ROOT_DIR") + message(FATAL_ERROR "QUIC support is missing in OpenSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR") endif() endfunction() option(USE_NGTCP2 "Use ngtcp2 and nghttp3 libraries for HTTP/3 support" OFF) if(USE_NGTCP2) - if(USE_OPENSSL) - if(HAVE_BORINGSSL) + if(USE_OPENSSL OR USE_WOLFSSL) + if(USE_WOLFSSL) + find_package(NGTCP2 REQUIRED wolfSSL) + elseif(HAVE_BORINGSSL) find_package(NGTCP2 REQUIRED BoringSSL) else() find_package(NGTCP2 REQUIRED OpenSSL) @@ -686,7 +754,7 @@ if(USE_NGTCP2) # TODO add GnuTLS support as vtls library. find_package(NGTCP2 REQUIRED GnuTLS) else() - message(FATAL_ERROR "ngtcp2 requires OpenSSL or GnuTLS") + message(FATAL_ERROR "ngtcp2 requires OpenSSL, wolfSSL or GnuTLS") endif() set(USE_NGTCP2 ON) include_directories(${NGTCP2_INCLUDE_DIRS}) @@ -734,6 +802,8 @@ if(NOT CURL_DISABLE_LDAP) check_library_exists_concat("wldap32" cldap_open HAVE_WLDAP32) if(NOT HAVE_WLDAP32) set(USE_WIN32_LDAP OFF) + elseif(NOT CURL_DISABLE_LDAPS) + set(HAVE_LDAP_SSL ON) endif() endif() endif() @@ -849,56 +919,6 @@ if(WIN32) endif() endif() -set(HAVE_LIBZ OFF) -set(USE_ZLIB OFF) -#optional_dependency(ZLIB) -find_package(ZLIB) -if(ZLIB_FOUND) - set(HAVE_LIBZ ON) - set(USE_ZLIB ON) - - # Depend on ZLIB via imported targets if supported by the running - # version of CMake. This allows our dependents to get our dependencies - # transitively. - if(NOT CMAKE_VERSION VERSION_LESS 3.4) - list(APPEND CURL_LIBS ZLIB::ZLIB) - else() - list(APPEND CURL_LIBS ${ZLIB_LIBRARIES}) - include_directories(${ZLIB_INCLUDE_DIRS}) - list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS}) - endif() -endif() - -option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF) -set(HAVE_BROTLI OFF) -if(CURL_BROTLI) - find_package(Brotli QUIET) - if(BROTLI_FOUND) - set(HAVE_BROTLI ON) - list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) - include_directories(${BROTLI_INCLUDE_DIRS}) - list(APPEND CMAKE_REQUIRED_INCLUDES ${BROTLI_INCLUDE_DIRS}) - endif() -endif() - -option(CURL_ZSTD "Set to ON to enable building curl with zstd support." OFF) -set(HAVE_ZSTD OFF) -if(CURL_ZSTD) - find_package(Zstd REQUIRED) - if (NOT DEFINED HAVE_ZSTD_CREATEDSTREAM) - cmake_push_check_state() - set(CMAKE_REQUIRED_INCLUDES ${Zstd_INCLUDE_DIRS}) - set(CMAKE_REQUIRED_LIBRARIES ${Zstd_LIBRARIES}) - check_symbol_exists(ZSTD_createDStream "zstd.h" HAVE_ZSTD_CREATEDSTREAM) - cmake_pop_check_state() - endif() - if(Zstd_FOUND AND HAVE_ZSTD_CREATEDSTREAM) - set(HAVE_ZSTD ON) - list(APPEND CURL_LIBS ${Zstd_LIBRARIES}) - include_directories(${Zstd_INCLUDE_DIRS}) - endif() -endif() - #libpsl option(CURL_USE_LIBPSL "Use libPSL" ON) mark_as_advanced(CURL_USE_LIBPSL) @@ -1035,7 +1055,9 @@ elseif("${CURL_CA_BUNDLE}" STREQUAL "none") unset(CURL_CA_BUNDLE CACHE) elseif("${CURL_CA_BUNDLE}" STREQUAL "auto") unset(CURL_CA_BUNDLE CACHE) - set(CURL_CA_BUNDLE_AUTODETECT TRUE) + if(NOT CMAKE_CROSSCOMPILING) + set(CURL_CA_BUNDLE_AUTODETECT TRUE) + endif() else() set(CURL_CA_BUNDLE_SET TRUE) endif() @@ -1046,7 +1068,7 @@ elseif("${CURL_CA_PATH}" STREQUAL "none") unset(CURL_CA_PATH CACHE) elseif("${CURL_CA_PATH}" STREQUAL "auto") unset(CURL_CA_PATH CACHE) - if(NOT USE_NSS) + if(NOT CMAKE_CROSSCOMPILING AND NOT USE_NSS) set(CURL_CA_PATH_AUTODETECT TRUE) endif() else() @@ -1156,7 +1178,6 @@ check_include_file_concat("unistd.h" HAVE_UNISTD_H) check_include_file_concat("utime.h" HAVE_UTIME_H) check_include_file_concat("stddef.h" HAVE_STDDEF_H) -check_include_file_concat("stdint.h" HAVE_STDINT_H) check_include_file_concat("sys/utsname.h" HAVE_SYS_UTSNAME_H) check_type_size(size_t SIZEOF_SIZE_T) @@ -1208,7 +1229,6 @@ check_symbol_exists(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R) check_symbol_exists(signal "${CURL_INCLUDES}" HAVE_SIGNAL) check_symbol_exists(strtoll "${CURL_INCLUDES}" HAVE_STRTOLL) -check_symbol_exists(_strtoi64 "${CURL_INCLUDES}" HAVE__STRTOI64) check_symbol_exists(strerror_r "${CURL_INCLUDES}" HAVE_STRERROR_R) check_symbol_exists(siginterrupt "${CURL_INCLUDES}" HAVE_SIGINTERRUPT) check_symbol_exists(getaddrinfo "${CURL_INCLUDES}" HAVE_GETADDRINFO) @@ -1415,8 +1435,6 @@ else() set(CURL_PULL_SYS_SOCKET_H ${HAVE_SYS_SOCKET_H}) set(CURL_PULL_SYS_POLL_H ${HAVE_SYS_POLL_H}) endif() -set(CURL_PULL_STDINT_H ${HAVE_STDINT_H}) -set(CURL_PULL_INTTYPES_H ${HAVE_INTTYPES_H}) include(CMake/OtherTests.cmake) diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index c5ce0a5..4c6288d 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -34,11 +34,12 @@ #endif /* Compile-time deprecation macros. */ -#if defined(__GNUC__) && (__GNUC__ >= 6) && \ +#if defined(__GNUC__) && \ + ((__GNUC__ > 12) || ((__GNUC__ == 12) && (__GNUC_MINOR__ >= 1 ))) && \ !defined(__INTEL_COMPILER) && \ !defined(CURL_DISABLE_DEPRECATION) && !defined(BUILDING_LIBCURL) -#define CURL_DEPRECATED(version, message) \ - __attribute__((deprecated("since " # version ". " message))) +#define CURL_DEPRECATED(version, message) \ + __attribute__((deprecated("since " # version ". " message))) #define CURL_IGNORE_DEPRECATION(statements) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index ce39cdc..6f2affa 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h @@ -32,12 +32,12 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.88.1" +#define LIBCURL_VERSION "8.0.1" /* The numeric version number is also available "in parts" by using these defines: */ -#define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 88 +#define LIBCURL_VERSION_MAJOR 8 +#define LIBCURL_VERSION_MINOR 0 #define LIBCURL_VERSION_PATCH 1 /* This is the numeric version of the libcurl version number, meant for easier @@ -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 0x075801 +#define LIBCURL_VERSION_NUM 0x080001 /* * This is the date and time when the full source package was created. The diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index b97b534..b3504b6 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h @@ -117,14 +117,14 @@ CURL_EXTERN void curl_url_cleanup(CURLU *handle); * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new * handle must also be freed with curl_url_cleanup(). */ -CURL_EXTERN CURLU *curl_url_dup(CURLU *in); +CURL_EXTERN CURLU *curl_url_dup(const CURLU *in); /* * curl_url_get() extracts a specific part of the URL from a CURLU * handle. Returns error code. The returned pointer MUST be freed with * curl_free() afterwards. */ -CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_get(const CURLU *handle, CURLUPart what, char **part, unsigned int flags); /* diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 15853ff..c3eb842 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -47,29 +47,6 @@ if(WIN32 AND NOT CURL_STATICLIB) list(APPEND CSOURCES libcurl.rc) endif() -# SET(CSOURCES -# # memdebug.c -not used -# # nwlib.c - Not used -# # strtok.c - specify later -# # strtoofft.c - specify later -# ) - -# #OPTION(CURL_MALLOC_DEBUG "Debug mallocs in Curl" OFF) -# MARK_AS_ADVANCED(CURL_MALLOC_DEBUG) -# IF(CURL_MALLOC_DEBUG) -# SET(CSOURCES ${CSOURCES} -# memdebug.c -# ) -# ENDIF(CURL_MALLOC_DEBUG) - -# # only build compat strtoofft if we need to -# IF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64) -# SET(CSOURCES ${CSOURCES} -# strtoofft.c -# ) -# ENDIF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64) - - # The rest of the build include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include) diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index c28b475..663190a 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc @@ -107,7 +107,7 @@ LIB_CFILES = \ base64.c \ bufref.c \ c-hyper.c \ - cf-http.c \ + cf-https-connect.c \ cf-socket.c \ cfilters.c \ conncache.c \ @@ -223,7 +223,6 @@ LIB_CFILES = \ version.c \ version_win32.c \ warnless.c \ - wildcard.c \ ws.c LIB_HFILES = \ @@ -233,7 +232,7 @@ LIB_HFILES = \ asyn.h \ bufref.h \ c-hyper.h \ - cf-http.h \ + cf-https-connect.h \ cf-socket.h \ cfilters.h \ conncache.h \ @@ -352,7 +351,6 @@ LIB_HFILES = \ urldata.h \ version_win32.h \ warnless.h \ - wildcard.h \ ws.h LIB_RCFILES = libcurl.rc diff --git a/Utilities/cmcurl/lib/cf-http.c b/Utilities/cmcurl/lib/cf-https-connect.c index 2ee3d4d..ed70ad0 100644 --- a/Utilities/cmcurl/lib/cf-http.c +++ b/Utilities/cmcurl/lib/cf-https-connect.c @@ -32,7 +32,7 @@ #include "cfilters.h" #include "connect.h" #include "multiif.h" -#include "cf-http.h" +#include "cf-https-connect.h" #include "http2.h" #include "vquic/vquic.h" @@ -266,7 +266,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); } else if(ctx->h21_baller.enabled) - cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", TRNSPRT_TCP); + cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", + cf->conn->transport); ctx->state = CF_HC_CONNECT; /* FALLTHROUGH */ @@ -280,7 +281,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } if(time_to_start_h21(cf, data, now)) { - cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", TRNSPRT_TCP); + cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", + cf->conn->transport); } if(cf_hc_baller_is_active(&ctx->h21_baller)) { @@ -374,6 +376,55 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf, || cf_hc_baller_data_pending(&ctx->h21_baller, data); } +static struct curltime get_max_baller_time(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query) +{ + struct cf_hc_ctx *ctx = cf->ctx; + struct Curl_cfilter *cfb; + struct curltime t, tmax; + + 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; + } + return tmax; +} + +static CURLcode cf_hc_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + if(!cf->connected) { + switch(query) { + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); + return CURLE_OK; + } + default: + break; + } + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) { DEBUGF(LOG_CF(data, cf, "close")); @@ -411,7 +462,7 @@ struct Curl_cftype Curl_cft_http_connect = { Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, + cf_hc_query, }; static CURLcode cf_hc_create(struct Curl_cfilter **pcf, diff --git a/Utilities/cmcurl/lib/cf-http.h b/Utilities/cmcurl/lib/cf-https-connect.h index 6a39527..6a39527 100644 --- a/Utilities/cmcurl/lib/cf-http.h +++ b/Utilities/cmcurl/lib/cf-https-connect.h diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c index 2549f34..6d9ace4 100644 --- a/Utilities/cmcurl/lib/cf-socket.c +++ b/Utilities/cmcurl/lib/cf-socket.c @@ -253,19 +253,6 @@ static CURLcode socket_open(struct Curl_easy *data, else { /* opensocket callback not set, so simply create the socket now */ *sockfd = socket(addr->family, addr->socktype, addr->protocol); - if(!*sockfd && addr->socktype == SOCK_DGRAM) { - /* This is icky and seems, at least, to happen on macOS: - * we get sockfd == 0 and if called again, we get a valid one > 0. - * If we close the 0, we sometimes get failures in multi poll, as - * 0 seems also be the fd for the sockpair used for WAKEUP polling. - * Very strange. Maybe this code should be ifdef'ed for macOS, but - * on "real" OS, fd 0 is stdin and we never see that. So... - */ - fake_sclose(*sockfd); - *sockfd = socket(addr->family, addr->socktype, addr->protocol); - DEBUGF(infof(data, "QUIRK: UDP socket() gave handle 0, 2nd attempt %d", - (int)*sockfd)); - } } if(*sockfd == CURL_SOCKET_BAD) @@ -338,20 +325,6 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, return socket_close(data, conn, FALSE, sock); } -bool Curl_socket_is_dead(curl_socket_t sock) -{ - int sval; - bool ret_val = TRUE; - - sval = SOCKET_READABLE(sock, 0); - if(sval == 0) - /* timeout */ - ret_val = FALSE; - - return ret_val; -} - - #ifdef USE_WINSOCK /* When you run a program that uses the Windows Sockets API, you may experience slow performance when you copy data to a TCP server. @@ -522,7 +495,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, conn->ip_version = CURL_IPRESOLVE_V6; #endif - rc = Curl_resolv(data, dev, 0, FALSE, &h); + rc = Curl_resolv(data, dev, 80, FALSE, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_resolver_wait_resolv(data, &h); conn->ip_version = ipver; @@ -1084,6 +1057,11 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, if(result) goto out; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + /* Connect TCP socket */ rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); if(-1 == rc) { @@ -1449,22 +1427,6 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, case CF_CTRL_CONN_INFO_UPDATE: cf_socket_active(cf, data); break; - case CF_CTRL_CONN_REPORT_STATS: - switch(ctx->transport) { - case TRNSPRT_UDP: - case TRNSPRT_QUIC: - /* Since UDP connected sockets work different from TCP, we use the - * time of the first byte from the peer as the "connect" time. */ - if(ctx->got_first_byte) { - Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at); - break; - } - /* FALLTHROUGH */ - default: - Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->connected_at); - break; - } - break; case CF_CTRL_DATA_SETUP: Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); break; @@ -1473,38 +1435,39 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, } static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + bool *input_pending) { struct cf_socket_ctx *ctx = cf->ctx; - int sval; + struct pollfd pfd[1]; + int r; + *input_pending = FALSE; (void)data; if(!ctx || ctx->sock == CURL_SOCKET_BAD) return FALSE; - sval = SOCKET_READABLE(ctx->sock, 0); - if(sval == 0) { - /* timeout */ - return TRUE; - } - else if(sval & CURL_CSELECT_ERR) { - /* socket is in an error state */ + /* Check with 0 timeout if there are any events pending on the socket */ + pfd[0].fd = ctx->sock; + pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; + pfd[0].revents = 0; + + r = Curl_poll(pfd, 1, 0); + if(r < 0) { + DEBUGF(LOG_CF(data, cf, "is_alive: poll error, assume dead")); return FALSE; } - else if(sval & CURL_CSELECT_IN) { - /* readable with no error. could still be closed */ -/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */ -#ifdef MSG_PEEK - /* use the socket */ - char buf; - if(recv((RECV_TYPE_ARG1)ctx->sock, (RECV_TYPE_ARG2)&buf, - (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) { - return FALSE; /* FIN received */ - } -#endif + else if(r == 0) { + DEBUGF(LOG_CF(data, cf, "is_alive: poll timeout, assume alive")); return TRUE; } + else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) { + DEBUGF(LOG_CF(data, cf, "is_alive: err/hup/etc events, assume dead")); + return FALSE; + } + DEBUGF(LOG_CF(data, cf, "is_alive: valid events, looks alive")); + *input_pending = TRUE; return TRUE; } @@ -1527,6 +1490,24 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, else *pres1 = -1; return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + switch(ctx->transport) { + case TRNSPRT_UDP: + case TRNSPRT_QUIC: + /* Since UDP connected sockets work different from TCP, we use the + * time of the first byte from the peer as the "connect" time. */ + if(ctx->got_first_byte) { + *when = ctx->first_byte_at; + break; + } + /* FALLTHROUGH */ + default: + *when = ctx->connected_at; + break; + } + return CURLE_OK; + } default: break; } @@ -1826,7 +1807,6 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, Curl_conn_cf_add(data, conn, sockindex, cf); conn->sock[sockindex] = ctx->sock; - set_remote_ip(cf, data); set_local_ip(cf, data); ctx->active = TRUE; ctx->connected_at = Curl_now(); @@ -1841,6 +1821,38 @@ out: return result; } +static void set_accepted_remote_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; +#ifdef HAVE_GETPEERNAME + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssrem; + curl_socklen_t plen; + + ctx->r_ip[0] = 0; + ctx->r_port = 0; + plen = sizeof(ssrem); + memset(&ssrem, 0, plen); + if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { + int error = SOCKERRNO; + failf(data, "getpeername() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; + } + if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + ctx->r_ip, &ctx->r_port)) { + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } +#else + ctx->r_ip[0] = 0; + ctx->r_port = 0; + (void)data; +#endif +} + CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, struct connectdata *conn, int sockindex, curl_socket_t *s) @@ -1857,13 +1869,14 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, socket_close(data, conn, TRUE, ctx->sock); ctx->sock = *s; conn->sock[sockindex] = ctx->sock; - set_remote_ip(cf, data); + set_accepted_remote_ip(cf, data); set_local_ip(cf, data); ctx->active = TRUE; ctx->accepted = TRUE; ctx->connected_at = Curl_now(); cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_accepted_set(%d)", (int)ctx->sock)); + DEBUGF(LOG_CF(data, cf, "accepted_set(sock=%d, remote=%s port=%d)", + (int)ctx->sock, ctx->r_ip, ctx->r_port)); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h index f6eb810..0eec61a 100644 --- a/Utilities/cmcurl/lib/cf-socket.h +++ b/Utilities/cmcurl/lib/cf-socket.h @@ -70,13 +70,6 @@ CURLcode Curl_socket_open(struct Curl_easy *data, int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sock); -/* - * This function should return TRUE if the socket is to be assumed to - * be dead. Most commonly this happens when the server has closed the - * connection due to inactivity. - */ -bool Curl_socket_is_dead(curl_socket_t sock); - /** * Determine the curl code for a socket connect() == -1 with errno. */ diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index 2af0dd8..e60d138 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c @@ -124,10 +124,11 @@ ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + bool *input_pending) { return cf->next? - cf->next->cft->is_alive(cf->next, data) : + cf->next->cft->is_alive(cf->next, data, input_pending) : FALSE; /* pessimistic in absence of data */ } @@ -370,9 +371,12 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, result = cf->cft->connect(cf, data, blocking, done); if(!result && *done) { Curl_conn_ev_update_info(data, data->conn); - Curl_conn_ev_report_stats(data, data->conn); + Curl_conn_report_connect_stats(data, data->conn); data->conn->keepalive = Curl_now(); } + else if(result) { + Curl_conn_report_connect_stats(data, data->conn); + } } return result; @@ -608,16 +612,32 @@ void Curl_conn_ev_update_info(struct Curl_easy *data, cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); } -void Curl_conn_ev_report_stats(struct Curl_easy *data, - struct connectdata *conn) +void Curl_conn_report_connect_stats(struct Curl_easy *data, + struct connectdata *conn) { - cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_REPORT_STATS, 0, NULL); + struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; + if(cf) { + struct curltime connected; + struct curltime appconnected; + + memset(&connected, 0, sizeof(connected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); + if(connected.tv_sec || connected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); + + memset(&appconnected, 0, sizeof(appconnected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); + if(appconnected.tv_sec || appconnected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); + } } -bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn) +bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, + bool *input_pending) { struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; - return cf && !cf->conn->bits.close && cf->cft->is_alive(cf, data); + return cf && !cf->conn->bits.close && + cf->cft->is_alive(cf, data, input_pending); } CURLcode Curl_conn_keep_alive(struct Curl_easy *data, diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h index 94dc53f..317f2bb 100644 --- a/Utilities/cmcurl/lib/cfilters.h +++ b/Utilities/cmcurl/lib/cfilters.h @@ -85,7 +85,8 @@ typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf, CURLcode *err); /* error to return */ typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data, + bool *input_pending); typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, struct Curl_easy *data); @@ -109,8 +110,6 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, #define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */ /* update conn info at connection and data */ #define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */ -/* report conn statistics (timers) for connection and data */ -#define CF_CTRL_CONN_REPORT_STATS (256+1) /* 0 NULL ignored */ /** * Handle event/control for the filter. @@ -138,6 +137,8 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, #define CF_QUERY_MAX_CONCURRENT 1 /* number - */ #define CF_QUERY_CONNECT_REPLY_MS 2 /* number - */ #define CF_QUERY_SOCKET 3 /* - curl_socket_t */ +#define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */ +#define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */ /** * Query the cfilter for properties. Filters ignorant of a query will @@ -216,7 +217,8 @@ CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, struct Curl_easy *data, int event, int arg1, void *arg2); bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data, + bool *input_pending); CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, @@ -435,15 +437,16 @@ void Curl_conn_ev_update_info(struct Curl_easy *data, struct connectdata *conn); /** - * Inform connection filters to report statistics. + * Update connection statistics */ -void Curl_conn_ev_report_stats(struct Curl_easy *data, - struct connectdata *conn); +void Curl_conn_report_connect_stats(struct Curl_easy *data, + struct connectdata *conn); /** * Check if FIRSTSOCKET's cfilter chain deems connection alive. */ -bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn); +bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, + bool *input_pending); /** * Try to upkeep the connection filters at sockindex. diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index c21b96c..1c736da 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c @@ -45,13 +45,6 @@ #define HASHKEY_SIZE 128 -static void conn_llist_dtor(void *user, void *element) -{ - struct connectdata *conn = element; - (void)user; - conn->bundle = NULL; -} - static CURLcode bundle_create(struct connectbundle **bundlep) { DEBUGASSERT(*bundlep == NULL); @@ -62,17 +55,12 @@ static CURLcode bundle_create(struct connectbundle **bundlep) (*bundlep)->num_connections = 0; (*bundlep)->multiuse = BUNDLE_UNKNOWN; - Curl_llist_init(&(*bundlep)->conn_list, (Curl_llist_dtor) conn_llist_dtor); + Curl_llist_init(&(*bundlep)->conn_list, NULL); return CURLE_OK; } static void bundle_destroy(struct connectbundle *bundle) { - if(!bundle) - return; - - Curl_llist_destroy(&bundle->conn_list, NULL); - free(bundle); } diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index 993a7f9..10d0c11 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -59,7 +59,7 @@ #include "strerror.h" #include "cfilters.h" #include "connect.h" -#include "cf-http.h" +#include "cf-https-connect.h" #include "cf-socket.h" #include "select.h" #include "url.h" /* for Curl_safefree() */ @@ -957,6 +957,28 @@ static bool cf_he_data_pending(struct Curl_cfilter *cf, return FALSE; } +static struct curltime get_max_baller_time(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query) +{ + struct cf_he_ctx *ctx = cf->ctx; + struct curltime t, tmax; + size_t i; + + memset(&tmax, 0, sizeof(tmax)); + for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + struct eyeballer *baller = ctx->baller[i]; + + memset(&t, 0, sizeof(t)); + if(baller && baller->cf && + !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) { + if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) + tmax = t; + } + } + return tmax; +} + static CURLcode cf_he_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) @@ -984,7 +1006,16 @@ static CURLcode cf_he_query(struct Curl_cfilter *cf, DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1)); return CURLE_OK; } - + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); + return CURLE_OK; + } default: break; } diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index a29d204..aa1e0cb 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c @@ -33,7 +33,15 @@ #endif #ifdef HAVE_BROTLI +#if defined(__GNUC__) +/* Ignore -Wvla warnings in brotli headers */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wvla" +#endif #include <brotli/decode.h> +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif #endif #ifdef HAVE_ZSTD diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index c457b2d..0c6e0f7 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -101,13 +101,14 @@ Example set of cookies: #include "parsedate.h" #include "rename.h" #include "fopen.h" +#include "strdup.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -static void strstore(char **str, const char *newstr); +static void strstore(char **str, const char *newstr, size_t len); static void freecookie(struct Cookie *co) { @@ -122,15 +123,17 @@ static void freecookie(struct Cookie *co) free(co); } -static bool tailmatch(const char *cooke_domain, const char *hostname) +static bool tailmatch(const char *cookie_domain, size_t cookie_domain_len, + const char *hostname) { - size_t cookie_domain_len = strlen(cooke_domain); size_t hostname_len = strlen(hostname); if(hostname_len < cookie_domain_len) return FALSE; - if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len)) + if(!strncasecompare(cookie_domain, + hostname + hostname_len-cookie_domain_len, + cookie_domain_len)) return FALSE; /* @@ -176,7 +179,7 @@ static bool pathmatch(const char *cookie_path, const char *request_uri) /* #-fragments are already cut off! */ if(0 == strlen(uri_path) || uri_path[0] != '/') { - strstore(&uri_path, "/"); + strstore(&uri_path, "/", 1); if(!uri_path) return FALSE; } @@ -310,7 +313,7 @@ static char *sanitize_cookie_path(const char *cookie_path) /* RFC6265 5.2.4 The Path Attribute */ if(new_path[0] != '/') { /* Let cookie-path be the default-path. */ - strstore(&new_path, "/"); + strstore(&new_path, "/", 1); return new_path; } @@ -333,10 +336,9 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); while(list) { - struct CookieInfo *newcookies = Curl_cookie_init(data, - list->data, - data->cookies, - data->set.cookiesession); + struct CookieInfo *newcookies = + Curl_cookie_init(data, list->data, data->cookies, + data->set.cookiesession); if(!newcookies) /* * Failure may be due to OOM or a bad cookie; both are ignored @@ -360,10 +362,14 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) * parsing in a last-wins scenario. The caller is responsible for checking * for OOM errors. */ -static void strstore(char **str, const char *newstr) +static void strstore(char **str, const char *newstr, size_t len) { + DEBUGASSERT(newstr); + DEBUGASSERT(str); free(*str); - *str = strdup(newstr); + *str = Curl_memdup(newstr, len + 1); + if(*str) + (*str)[len] = 0; } /* @@ -425,15 +431,19 @@ static void remove_expired(struct CookieInfo *cookies) } /* Make sure domain contains a dot or is localhost. */ -static bool bad_domain(const char *domain) +static bool bad_domain(const char *domain, size_t len) { - if(strcasecompare(domain, "localhost")) + if((len == 9) && strncasecompare(domain, "localhost", 9)) return FALSE; else { /* there must be a dot present, but that dot must not be a trailing dot */ - char *dot = strchr(domain, '.'); - if(dot) - return dot[1] ? FALSE : TRUE; + char *dot = memchr(domain, '.', len); + if(dot) { + size_t i = dot - domain; + if((len - i) > 1) + /* the dot is not the last byte */ + return FALSE; + } } return TRUE; } @@ -513,10 +523,9 @@ Curl_cookie_add(struct Curl_easy *data, if(httpheader) { /* This line was read off an HTTP-header */ - char name[MAX_NAME]; - char what[MAX_NAME]; + const char *namep; + const char *valuep; const char *ptr; - const char *semiptr; size_t linelength = strlen(lineptr); if(linelength > MAX_COOKIE_LINE) { @@ -525,73 +534,65 @@ Curl_cookie_add(struct Curl_easy *data, return NULL; } - semiptr = strchr(lineptr, ';'); /* first, find a semicolon */ - - while(*lineptr && ISBLANK(*lineptr)) - lineptr++; - ptr = lineptr; do { - /* we have a <what>=<this> pair or a stand-alone word here */ - name[0] = what[0] = 0; /* init the buffers */ - if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\t\r\n=] =%" - MAX_NAME_TXT "[^;\r\n]", - name, what)) { - /* - * Use strstore() below to properly deal with received cookie - * headers that have the same string property set more than once, - * and then we use the last one. - */ - const char *whatptr; + size_t vlen; + size_t nlen; + + while(*ptr && ISBLANK(*ptr)) + ptr++; + + /* we have a <name>=<value> pair or a stand-alone word here */ + nlen = strcspn(ptr, ";\t\r\n="); + if(nlen) { bool done = FALSE; - bool sep; - size_t len = strlen(what); - size_t nlen = strlen(name); - const char *endofn = &ptr[ nlen ]; + bool sep = FALSE; - /* - * Check for too long individual name or contents, or too long - * combination of name + contents. Chrome and Firefox support 4095 or - * 4096 bytes combo - */ - if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) || - ((nlen + len) > MAX_NAME)) { - freecookie(co); - infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - nlen, len); - return NULL; - } + namep = ptr; + ptr += nlen; - /* name ends with a '=' ? */ - sep = (*endofn == '=')?TRUE:FALSE; + /* trim trailing spaces and tabs after name */ + while(nlen && ISBLANK(namep[nlen - 1])) + nlen--; - if(nlen) { - endofn--; /* move to the last character */ - if(ISBLANK(*endofn)) { - /* skip trailing spaces in name */ - while(*endofn && ISBLANK(*endofn) && nlen) { - endofn--; - nlen--; - } - name[nlen] = 0; /* new end of name */ + if(*ptr == '=') { + vlen = strcspn(++ptr, ";\r\n"); + valuep = ptr; + sep = TRUE; + ptr = &valuep[vlen]; + + /* Strip off trailing whitespace from the value */ + while(vlen && ISBLANK(valuep[vlen-1])) + vlen--; + + /* Skip leading whitespace from the value */ + while(vlen && ISBLANK(*valuep)) { + valuep++; + vlen--; } - } - /* Strip off trailing whitespace from the 'what' */ - while(len && ISBLANK(what[len-1])) { - what[len-1] = 0; - len--; + /* Reject cookies with a TAB inside the value */ + if(memchr(valuep, '\t', vlen)) { + freecookie(co); + infof(data, "cookie contains TAB, dropping"); + return NULL; + } + } + else { + valuep = NULL; + vlen = 0; } - /* Skip leading whitespace from the 'what' */ - whatptr = what; - while(*whatptr && ISBLANK(*whatptr)) - whatptr++; - - /* Reject cookies with a TAB inside the content */ - if(strchr(whatptr, '\t')) { + /* + * Check for too long individual name or contents, or too long + * combination of name + contents. Chrome and Firefox support 4095 or + * 4096 bytes combo + */ + if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) || + ((nlen + vlen) > MAX_NAME)) { freecookie(co); - infof(data, "cookie contains TAB, dropping"); + infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", + nlen, vlen); return NULL; } @@ -601,13 +602,19 @@ Curl_cookie_add(struct Curl_easy *data, * "the rest". Prefixes must start with '__' and end with a '-', so * only test for names where that can possibly be true. */ - if(nlen > 3 && name[0] == '_' && name[1] == '_') { - if(strncasecompare("__Secure-", name, 9)) + if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') { + if(strncasecompare("__Secure-", namep, 9)) co->prefix |= COOKIE_PREFIX__SECURE; - else if(strncasecompare("__Host-", name, 7)) + else if(strncasecompare("__Host-", namep, 7)) co->prefix |= COOKIE_PREFIX__HOST; } + /* + * Use strstore() below to properly deal with received cookie + * headers that have the same string property set more than once, + * and then we use the last one. + */ + if(!co->name) { /* The very first name/value pair is the actual cookie name */ if(!sep) { @@ -615,20 +622,20 @@ Curl_cookie_add(struct Curl_easy *data, badcookie = TRUE; break; } - co->name = strdup(name); - co->value = strdup(whatptr); + strstore(&co->name, namep, nlen); + strstore(&co->value, valuep, vlen); done = TRUE; if(!co->name || !co->value) { badcookie = TRUE; break; } - if(invalid_octets(whatptr) || invalid_octets(name)) { + if(invalid_octets(co->value) || invalid_octets(co->name)) { infof(data, "invalid octets in name/value, cookie dropped"); badcookie = TRUE; break; } } - else if(!len) { + else if(!vlen) { /* * this was a "<name>=" with no content, and we must allow * 'secure' and 'httponly' specified this weirdly @@ -639,7 +646,7 @@ Curl_cookie_add(struct Curl_easy *data, * using a secure protocol, or when the cookie is being set by * reading from file */ - if(strcasecompare("secure", name)) { + if((nlen == 6) && strncasecompare("secure", namep, 6)) { if(secure || !c->running) { co->secure = TRUE; } @@ -648,7 +655,7 @@ Curl_cookie_add(struct Curl_easy *data, break; } } - else if(strcasecompare("httponly", name)) + else if((nlen == 8) && strncasecompare("httponly", namep, 8)) co->httponly = TRUE; else if(sep) /* there was a '=' so we're not done parsing this field */ @@ -656,8 +663,8 @@ Curl_cookie_add(struct Curl_easy *data, } if(done) ; - else if(strcasecompare("path", name)) { - strstore(&co->path, whatptr); + else if((nlen == 4) && strncasecompare("path", namep, 4)) { + strstore(&co->path, valuep, vlen); if(!co->path) { badcookie = TRUE; /* out of memory bad */ break; @@ -669,7 +676,8 @@ Curl_cookie_add(struct Curl_easy *data, break; } } - else if(strcasecompare("domain", name) && whatptr[0]) { + else if((nlen == 6) && + strncasecompare("domain", namep, 6) && vlen) { bool is_ip; /* @@ -677,8 +685,10 @@ Curl_cookie_add(struct Curl_easy *data, * the given domain is not valid and thus cannot be set. */ - if('.' == whatptr[0]) - whatptr++; /* ignore preceding dot */ + if('.' == valuep[0]) { + valuep++; /* ignore preceding dot */ + vlen--; + } #ifndef USE_LIBPSL /* @@ -686,16 +696,17 @@ Curl_cookie_add(struct Curl_easy *data, * TLD or otherwise "protected" suffix. To reduce risk, we require a * dot OR the exact host name being "localhost". */ - if(bad_domain(whatptr)) + if(bad_domain(valuep, vlen)) domain = ":"; #endif - is_ip = Curl_host_is_ipnum(domain ? domain : whatptr); + is_ip = Curl_host_is_ipnum(domain ? domain : valuep); if(!domain - || (is_ip && !strcmp(whatptr, domain)) - || (!is_ip && tailmatch(whatptr, domain))) { - strstore(&co->domain, whatptr); + || (is_ip && !strncmp(valuep, domain, vlen) && + (vlen == strlen(domain))) + || (!is_ip && tailmatch(valuep, vlen, domain))) { + strstore(&co->domain, valuep, vlen); if(!co->domain) { badcookie = TRUE; break; @@ -711,17 +722,17 @@ Curl_cookie_add(struct Curl_easy *data, */ badcookie = TRUE; infof(data, "skipped cookie with bad tailmatch domain: %s", - whatptr); + valuep); } } - else if(strcasecompare("version", name)) { - strstore(&co->version, whatptr); + else if((nlen == 7) && strncasecompare("version", namep, 7)) { + strstore(&co->version, valuep, vlen); if(!co->version) { badcookie = TRUE; break; } } - else if(strcasecompare("max-age", name)) { + else if((nlen == 7) && strncasecompare("max-age", namep, 7)) { /* * Defined in RFC2109: * @@ -731,14 +742,14 @@ Curl_cookie_add(struct Curl_easy *data, * client should discard the cookie. A value of zero means the * cookie should be discarded immediately. */ - strstore(&co->maxage, whatptr); + strstore(&co->maxage, valuep, vlen); if(!co->maxage) { badcookie = TRUE; break; } } - else if(strcasecompare("expires", name)) { - strstore(&co->expirestr, whatptr); + else if((nlen == 7) && strncasecompare("expires", namep, 7)) { + strstore(&co->expirestr, valuep, vlen); if(!co->expirestr) { badcookie = TRUE; break; @@ -753,24 +764,13 @@ Curl_cookie_add(struct Curl_easy *data, /* this is an "illegal" <what>=<this> pair */ } - if(!semiptr || !*semiptr) { - /* we already know there are no more cookies */ - semiptr = NULL; - continue; - } - - ptr = semiptr + 1; while(*ptr && ISBLANK(*ptr)) ptr++; - semiptr = strchr(ptr, ';'); /* now, find the next semicolon */ - - if(!semiptr && *ptr) - /* - * There are no more semicolons, but there's a final name=value pair - * coming up - */ - semiptr = strchr(ptr, '\0'); - } while(semiptr); + if(*ptr == ';') + ptr++; + else + break; + } while(1); if(co->maxage) { CURLofft offt; @@ -1057,7 +1057,7 @@ Curl_cookie_add(struct Curl_easy *data, Curl_psl_release(data); } else - acceptable = !bad_domain(domain); + acceptable = !bad_domain(domain, strlen(domain)); if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " @@ -1447,7 +1447,8 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, /* now check if the domain is correct */ if(!co->domain || - (co->tailmatch && !is_ip && tailmatch(co->domain, host)) || + (co->tailmatch && !is_ip && + tailmatch(co->domain, co->domain? strlen(co->domain):0, host)) || ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { /* * the right part of the host matches the domain stuff in the @@ -1798,11 +1799,6 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) CURLcode res; if(data->set.str[STRING_COOKIEJAR]) { - /* If there is a list of cookie files to read, do it first so that - we have all the told files read before we write the new jar. - Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ - Curl_cookie_loadfiles(data); - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); /* if we have a destination file for all the cookies to get dumped to */ diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c index c4c4f88..c6fe125 100644 --- a/Utilities/cmcurl/lib/curl_gssapi.c +++ b/Utilities/cmcurl/lib/curl_gssapi.c @@ -34,10 +34,16 @@ #include "curl_memory.h" #include "memdebug.h" -gss_OID_desc Curl_spnego_mech_oid = { +#if defined(__GNUC__) +#define CURL_ALIGN8 __attribute__ ((aligned(8))) +#else +#define CURL_ALIGN8 +#endif + +gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = { 6, (char *)"\x2b\x06\x01\x05\x05\x02" }; -gss_OID_desc Curl_krb5_mech_oid = { +gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = { 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; diff --git a/Utilities/cmcurl/lib/curl_log.c b/Utilities/cmcurl/lib/curl_log.c index b5ce1c7..2301cff 100644 --- a/Utilities/cmcurl/lib/curl_log.c +++ b/Utilities/cmcurl/lib/curl_log.c @@ -38,7 +38,7 @@ #include "connect.h" #include "http2.h" #include "http_proxy.h" -#include "cf-http.h" +#include "cf-https-connect.h" #include "socks.h" #include "strtok.h" #include "vtls/vtls.h" diff --git a/Utilities/cmcurl/lib/curl_path.c b/Utilities/cmcurl/lib/curl_path.c index 2df8687..977e533 100644 --- a/Utilities/cmcurl/lib/curl_path.c +++ b/Utilities/cmcurl/lib/curl_path.c @@ -32,70 +32,65 @@ #include "escape.h" #include "memdebug.h" +#define MAX_SSHPATH_LEN 100000 /* arbitrary */ + /* figure out the path to work with in this particular request */ CURLcode Curl_getworkingpath(struct Curl_easy *data, char *homedir, /* when SFTP is used */ char **path) /* returns the allocated real path to work with */ { - char *real_path = NULL; char *working_path; size_t working_path_len; + struct dynbuf npath; CURLcode result = Curl_urldecode(data->state.up.path, 0, &working_path, &working_path_len, REJECT_ZERO); if(result) return result; + /* new path to switch to in case we need to */ + Curl_dyn_init(&npath, MAX_SSHPATH_LEN); + /* Check for /~/, indicating relative to the user's home directory */ - if(data->conn->handler->protocol & CURLPROTO_SCP) { - real_path = malloc(working_path_len + 1); - if(!real_path) { + if((data->conn->handler->protocol & CURLPROTO_SCP) && + (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) { + /* It is referenced to the home directory, so strip the leading '/~/' */ + if(Curl_dyn_addn(&npath, &working_path[3], working_path_len - 3)) { free(working_path); return CURLE_OUT_OF_MEMORY; } - if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) - /* It is referenced to the home directory, so strip the leading '/~/' */ - memcpy(real_path, working_path + 3, working_path_len - 2); - else - memcpy(real_path, working_path, 1 + working_path_len); } - else if(data->conn->handler->protocol & CURLPROTO_SFTP) { - if((working_path_len > 1) && (working_path[1] == '~')) { - size_t homelen = strlen(homedir); - real_path = malloc(homelen + working_path_len + 1); - if(!real_path) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - /* It is referenced to the home directory, so strip the - leading '/' */ - memcpy(real_path, homedir, homelen); - /* Only add a trailing '/' if homedir does not end with one */ - if(homelen == 0 || real_path[homelen - 1] != '/') { - real_path[homelen] = '/'; - homelen++; - real_path[homelen] = '\0'; - } - if(working_path_len > 3) { - memcpy(real_path + homelen, working_path + 3, - 1 + working_path_len -3); - } + else if((data->conn->handler->protocol & CURLPROTO_SFTP) && + (working_path_len > 2) && !memcmp(working_path, "/~/", 3)) { + size_t len; + const char *p; + int copyfrom = 3; + if(Curl_dyn_add(&npath, homedir)) { + free(working_path); + return CURLE_OUT_OF_MEMORY; } - else { - real_path = malloc(working_path_len + 1); - if(!real_path) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - memcpy(real_path, working_path, 1 + working_path_len); + /* Copy a separating '/' if homedir does not end with one */ + len = Curl_dyn_len(&npath); + p = Curl_dyn_ptr(&npath); + if(len && (p[len-1] != '/')) + copyfrom = 2; + + if(Curl_dyn_addn(&npath, + &working_path[copyfrom], working_path_len - copyfrom)) { + free(working_path); + return CURLE_OUT_OF_MEMORY; } } - free(working_path); + if(Curl_dyn_len(&npath)) { + free(working_path); - /* store the pointer for the caller to receive */ - *path = real_path; + /* store the pointer for the caller to receive */ + *path = Curl_dyn_ptr(&npath); + } + else + *path = working_path; return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index b58f813..06fb613 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -456,8 +456,8 @@ # endif #endif -#if (SIZEOF_CURL_OFF_T == 4) -# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) +#if (SIZEOF_CURL_OFF_T < 8) +#error "too small curl_off_t" #else /* assume SIZEOF_CURL_OFF_T == 8 */ # define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h index e68eb50..dde7229 100644 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ b/Utilities/cmcurl/lib/curl_setup_once.h @@ -69,6 +69,14 @@ #include <unistd.h> #endif +#ifdef USE_WOLFSSL +# if defined(HAVE_STDINT_H) +# include <stdint.h> +# elif defined(HAVE_INTTYPES_H) +# include <inttypes.h> +# endif +#endif + #ifdef __hpux # if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) # ifdef _APP32_64BIT_OFF_T diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index 2e6a377..922d757 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c @@ -952,7 +952,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* we got a response, store it in the cache */ - dns = Curl_cache_addr(data, ai, dohp->host, dohp->port); + dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c index 847f6f6..bd3b935 100644 --- a/Utilities/cmcurl/lib/dynbuf.c +++ b/Utilities/cmcurl/lib/dynbuf.c @@ -99,8 +99,7 @@ static CURLcode dyn_nappend(struct dynbuf *s, include that as well when it uses this code */ void *p = realloc(s->bufr, a); if(!p) { - Curl_safefree(s->bufr); - s->leng = s->allc = 0; + Curl_dyn_free(s); return CURLE_OUT_OF_MEMORY; } s->bufr = p; diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index db03026..27124a7 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -1228,7 +1228,6 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, return result; *n = (size_t)n1; - infof(data, "reached %s:%d", __FILE__, __LINE__); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 8c72874..ef9ec1e 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -436,6 +436,12 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) bool connected; DEBUGF(infof(data, "ftp InitiateTransfer()")); + if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && + !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); + if(result) + return result; + } result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected); if(result || !connected) return result; @@ -1795,6 +1801,29 @@ static char *control_address(struct connectdata *conn) return conn->primary_ip; } +static bool match_pasv_6nums(const char *p, + unsigned int *array) /* 6 numbers */ +{ + int i; + for(i = 0; i < 6; i++) { + unsigned long num; + char *endp; + if(i) { + if(*p != ',') + return FALSE; + p++; + } + if(!ISDIGIT(*p)) + return FALSE; + num = strtoul(p, &endp, 10); + if(num > 255) + return FALSE; + array[i] = (unsigned int)num; + p = endp; + } + return TRUE; +} + static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, int ftpcode) { @@ -1814,27 +1843,18 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* positive EPSV response */ char *ptr = strchr(str, '('); if(ptr) { - unsigned int num; - char separator[4]; + char sep; ptr++; - if(5 == sscanf(ptr, "%c%c%c%u%c", - &separator[0], - &separator[1], - &separator[2], - &num, - &separator[3])) { - const char sep1 = separator[0]; - int i; - - /* The four separators should be identical, or else this is an oddly - formatted reply and we bail out immediately. */ - for(i = 1; i<4; i++) { - if(separator[i] != sep1) { - ptr = NULL; /* set to NULL to signal error */ - break; - } - } - if(num > 0xffff) { + /* |||12345| */ + sep = ptr[0]; + /* the ISDIGIT() check here is because strtoul() accepts leading minus + etc */ + if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { + char *endp; + unsigned long num = strtoul(&ptr[3], &endp, 10); + if(*endp != sep) + ptr = NULL; + else if(num > 0xffff) { failf(data, "Illegal port number in EPSV reply"); return CURLE_FTP_WEIRD_PASV_REPLY; } @@ -1856,8 +1876,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, else if((ftpc->count1 == 1) && (ftpcode == 227)) { /* positive PASV response */ - unsigned int ip[4] = {0, 0, 0, 0}; - unsigned int port[2] = {0, 0}; + unsigned int ip[6]; /* * Scan for a sequence of six comma-separated numbers and use them as @@ -1869,15 +1888,12 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, * "227 Entering passive mode. 127,0,0,1,4,51" */ while(*str) { - if(6 == sscanf(str, "%u,%u,%u,%u,%u,%u", - &ip[0], &ip[1], &ip[2], &ip[3], - &port[0], &port[1])) + if(match_pasv_6nums(str, ip)) break; str++; } - if(!*str || (ip[0] > 255) || (ip[1] > 255) || (ip[2] > 255) || - (ip[3] > 255) || (port[0] > 255) || (port[1] > 255) ) { + if(!*str) { failf(data, "Couldn't interpret the 227-response"); return CURLE_FTP_WEIRD_227_FORMAT; } @@ -1897,7 +1913,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, if(!ftpc->newhost) return CURLE_OUT_OF_MEMORY; - ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff); + ftpc->newport = (unsigned short)(((ip[4]<<8) + ip[5]) & 0xffff); } else if(ftpc->count1 == 0) { /* EPSV failed, move on to PASV */ @@ -2032,6 +2048,30 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data, return result; } +static int twodigit(const char *p) +{ + return (p[0]-'0') * 10 + (p[1]-'0'); +} + +static bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second) +{ + size_t len = strlen(p); + if(len < 14) + return FALSE; + *year = twodigit(&p[0]) * 100 + twodigit(&p[2]); + *month = twodigit(&p[4]); + *day = twodigit(&p[6]); + *hour = twodigit(&p[8]); + *minute = twodigit(&p[10]); + *second = twodigit(&p[12]); + + if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) || + (*second > 60)) + return FALSE; + return TRUE; +} + static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, int ftpcode) { @@ -2046,8 +2086,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the last .sss part is optional and means fractions of a second */ int year, month, day, hour, minute, second; - if(6 == sscanf(&data->state.buffer[4], "%04d%02d%02d%02d%02d%02d", - &year, &month, &day, &hour, &minute, &second)) { + if(ftp_213_date(&data->state.buffer[4], + &year, &month, &day, &hour, &minute, &second)) { /* we have a time, reformat it */ char timebuf[24]; msnprintf(timebuf, sizeof(timebuf), @@ -2635,7 +2675,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, int ftpcode; struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; - static const char ftpauth[][4] = { "SSL", "TLS" }; + static const char * const ftpauth[] = { "SSL", "TLS" }; size_t nread = 0; if(pp->sendleft) @@ -3221,7 +3261,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { Curl_set_in_callback(data, true); - data->set.chunk_end(data->wildcard.customptr); + data->set.chunk_end(data->set.wildcardptr); Curl_set_in_callback(data, false); } ftpc->known_filesize = -1; @@ -3728,7 +3768,7 @@ static CURLcode init_wc_data(struct Curl_easy *data) char *last_slash; struct FTP *ftp = data->req.p.ftp; char *path = ftp->path; - struct WildcardData *wildcard = &(data->wildcard); + struct WildcardData *wildcard = data->wildcard; CURLcode result = CURLE_OK; struct ftp_wc *ftpwc = NULL; @@ -3776,7 +3816,7 @@ static CURLcode init_wc_data(struct Curl_easy *data) goto fail; } - wildcard->protdata = ftpwc; /* put it to the WildcardData tmp pointer */ + wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */ wildcard->dtor = wc_data_dtor; /* wildcard does not support NOCWD option (assert it?) */ @@ -3814,13 +3854,13 @@ static CURLcode init_wc_data(struct Curl_easy *data) } Curl_safefree(wildcard->pattern); wildcard->dtor = ZERO_NULL; - wildcard->protdata = NULL; + wildcard->ftpwc = NULL; return result; } static CURLcode wc_statemach(struct Curl_easy *data) { - struct WildcardData * const wildcard = &(data->wildcard); + struct WildcardData * const wildcard = data->wildcard; struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; @@ -3837,7 +3877,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) case CURLWC_MATCHING: { /* In this state is LIST response successfully parsed, so lets restore previous WRITEFUNCTION callback and WRITEDATA pointer */ - struct ftp_wc *ftpwc = wildcard->protdata; + struct ftp_wc *ftpwc = wildcard->ftpwc; data->set.fwrite_func = ftpwc->backup.write_function; data->set.out = ftpwc->backup.file_descriptor; ftpwc->backup.write_function = ZERO_NULL; @@ -3876,7 +3916,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) long userresponse; Curl_set_in_callback(data, true); userresponse = data->set.chunk_bgn( - finfo, wildcard->customptr, (int)wildcard->filelist.size); + finfo, data->set.wildcardptr, (int)wildcard->filelist.size); Curl_set_in_callback(data, false); switch(userresponse) { case CURL_CHUNK_BGN_FUNC_SKIP: @@ -3916,7 +3956,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) case CURLWC_SKIP: { if(data->set.chunk_end) { Curl_set_in_callback(data, true); - data->set.chunk_end(data->wildcard.customptr); + data->set.chunk_end(data->set.wildcardptr); Curl_set_in_callback(data, false); } Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); @@ -3926,7 +3966,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) } case CURLWC_CLEAN: { - struct ftp_wc *ftpwc = wildcard->protdata; + struct ftp_wc *ftpwc = wildcard->ftpwc; result = CURLE_OK; if(ftpwc) result = Curl_ftp_parselist_geterror(ftpwc->parser); @@ -3939,7 +3979,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) case CURLWC_ERROR: case CURLWC_CLEAR: if(wildcard->dtor) - wildcard->dtor(wildcard->protdata); + wildcard->dtor(wildcard->ftpwc); return result; } } @@ -3966,8 +4006,8 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done) if(data->state.wildcardmatch) { result = wc_statemach(data); - if(data->wildcard.state == CURLWC_SKIP || - data->wildcard.state == CURLWC_DONE) { + if(data->wildcard->state == CURLWC_SKIP || + data->wildcard->state == CURLWC_DONE) { /* do not call ftp_regular_transfer */ return CURLE_OK; } @@ -4053,6 +4093,8 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, } freedirs(ftpc); + Curl_safefree(ftpc->account); + Curl_safefree(ftpc->alternative_to_user); Curl_safefree(ftpc->prevpath); Curl_safefree(ftpc->server_os); Curl_pp_disconnect(pp); @@ -4322,11 +4364,31 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, char *type; struct FTP *ftp; CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = &conn->proto.ftpc; - data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1); + ftp = calloc(sizeof(struct FTP), 1); if(!ftp) return CURLE_OUT_OF_MEMORY; + /* clone connection related data that is FTP specific */ + if(data->set.str[STRING_FTP_ACCOUNT]) { + ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]); + if(!ftpc->account) { + free(ftp); + return CURLE_OUT_OF_MEMORY; + } + } + if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) { + ftpc->alternative_to_user = + strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); + if(!ftpc->alternative_to_user) { + Curl_safefree(ftpc->account); + free(ftp); + return CURLE_OUT_OF_MEMORY; + } + } + data->req.p.ftp = ftp; + ftp->path = &data->state.up.path[1]; /* don't include the initial slash */ /* FTP URLs support an extension like ";type=<typecode>" that @@ -4361,7 +4423,9 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, /* get some initial data into the ftp struct */ ftp->transfer = PPTRANSFER_BODY; ftp->downloadsize = 0; - conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ + ftpc->known_filesize = -1; /* unknown size for now */ + ftpc->use_ssl = data->set.use_ssl; + ftpc->ccc = data->set.ftp_ccc; return result; } diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h index 65efa6f..977fc88 100644 --- a/Utilities/cmcurl/lib/ftp.h +++ b/Utilities/cmcurl/lib/ftp.h @@ -120,6 +120,8 @@ struct FTP { struct */ struct ftp_conn { struct pingpong pp; + char *account; + char *alternative_to_user; char *entrypath; /* the PWD reply when we logged on */ char *file; /* url-decoded file name (or path) */ char **dirs; /* realloc()ed array for path components */ @@ -143,6 +145,9 @@ struct ftp_conn { ftpstate state; /* always use ftp.c:state() to change state! */ ftpstate state_saved; /* transfer type saved to be reloaded after data connection is established */ + 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 ccc; /* ccc level for this connection */ BIT(ftp_trying_alternative); BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) file size and 226/250 status check. It should still diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index f7c6434..39001e3 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c @@ -181,6 +181,43 @@ struct ftp_parselist_data { } offsets; }; +static void fileinfo_dtor(void *user, void *element) +{ + (void)user; + Curl_fileinfo_cleanup(element); +} + +CURLcode Curl_wildcard_init(struct WildcardData *wc) +{ + Curl_llist_init(&wc->filelist, fileinfo_dtor); + wc->state = CURLWC_INIT; + + return CURLE_OK; +} + +void Curl_wildcard_dtor(struct WildcardData **wcp) +{ + struct WildcardData *wc = *wcp; + if(!wc) + return; + + if(wc->dtor) { + wc->dtor(wc->ftpwc); + wc->dtor = ZERO_NULL; + wc->ftpwc = NULL; + } + DEBUGASSERT(wc->ftpwc == NULL); + + Curl_llist_destroy(&wc->filelist, NULL); + free(wc->path); + wc->path = NULL; + free(wc->pattern); + wc->pattern = NULL; + wc->state = CURLWC_INIT; + free(wc); + *wcp = NULL; +} + struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void) { return calloc(1, sizeof(struct ftp_parselist_data)); @@ -274,8 +311,8 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, struct fileinfo *infop) { curl_fnmatch_callback compare; - struct WildcardData *wc = &data->wildcard; - struct ftp_wc *ftpwc = wc->protdata; + struct WildcardData *wc = data->wildcard; + struct ftp_wc *ftpwc = wc->ftpwc; struct Curl_llist *llist = &wc->filelist; struct ftp_parselist_data *parser = ftpwc->parser; bool add = TRUE; @@ -330,7 +367,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, { size_t bufflen = size*nmemb; struct Curl_easy *data = (struct Curl_easy *)connptr; - struct ftp_wc *ftpwc = data->wildcard.protdata; + struct ftp_wc *ftpwc = data->wildcard->ftpwc; struct ftp_parselist_data *parser = ftpwc->parser; struct fileinfo *infop; struct curl_fileinfo *finfo; diff --git a/Utilities/cmcurl/lib/ftplistparser.h b/Utilities/cmcurl/lib/ftplistparser.h index 3d9a43b..5ba1f6a 100644 --- a/Utilities/cmcurl/lib/ftplistparser.h +++ b/Utilities/cmcurl/lib/ftplistparser.h @@ -39,5 +39,39 @@ struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void); void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data); +/* list of wildcard process states */ +typedef enum { + CURLWC_CLEAR = 0, + CURLWC_INIT = 1, + CURLWC_MATCHING, /* library is trying to get list of addresses for + downloading */ + CURLWC_DOWNLOADING, + CURLWC_CLEAN, /* deallocate resources and reset settings */ + CURLWC_SKIP, /* skip over concrete file */ + CURLWC_ERROR, /* error cases */ + CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop + will end */ +} wildcard_states; + +typedef void (*wildcard_dtor)(void *ptr); + +/* struct keeping information about wildcard download process */ +struct WildcardData { + char *path; /* path to the directory, where we trying wildcard-match */ + char *pattern; /* wildcard pattern */ + struct Curl_llist filelist; /* llist with struct Curl_fileinfo */ + struct ftp_wc *ftpwc; /* pointer to FTP wildcard data */ + wildcard_dtor dtor; + unsigned char state; /* wildcard_states */ +}; + +CURLcode Curl_wildcard_init(struct WildcardData *wc); +void Curl_wildcard_dtor(struct WildcardData **wcp); + +struct Curl_easy; + +#else +/* FTP is disabled */ +#define Curl_wildcard_dtor(x) #endif /* CURL_DISABLE_FTP */ #endif /* HEADER_CURL_FTPLISTPARSER_H */ diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c index 22e0e01..6cd7e31 100644 --- a/Utilities/cmcurl/lib/headers.c +++ b/Utilities/cmcurl/lib/headers.c @@ -38,14 +38,13 @@ /* Generate the curl_header struct for the user. This function MUST assign all struct fields in the output struct. */ -static void copy_header_external(struct Curl_easy *data, - struct Curl_header_store *hs, +static void copy_header_external(struct Curl_header_store *hs, size_t index, size_t amount, struct Curl_llist_element *e, - struct curl_header **hout) + struct curl_header *hout) { - struct curl_header *h = *hout = &data->state.headerout; + struct curl_header *h = hout; h->name = hs->name; h->value = hs->value; h->amount = amount; @@ -118,7 +117,9 @@ CURLHcode curl_easy_header(CURL *easy, return CURLHE_MISSING; } /* this is the name we want */ - copy_header_external(data, hs, nameindex, amount, e_pick, hout); + copy_header_external(hs, nameindex, amount, e_pick, + &data->state.headerout[0]); + *hout = &data->state.headerout[0]; return CURLHE_OK; } @@ -132,7 +133,6 @@ struct curl_header *curl_easy_nextheader(CURL *easy, struct Curl_llist_element *pick; struct Curl_llist_element *e; struct Curl_header_store *hs; - struct curl_header *hout; size_t amount = 0; size_t index = 0; @@ -179,8 +179,9 @@ struct curl_header *curl_easy_nextheader(CURL *easy, index = amount - 1; } - copy_header_external(data, hs, index, amount, pick, &hout); - return hout; + copy_header_external(hs, index, amount, pick, + &data->state.headerout[1]); + return &data->state.headerout[1]; } static CURLcode namevalue(char *header, size_t hlen, unsigned int type, diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c index 536eb73..2f6762c 100644 --- a/Utilities/cmcurl/lib/hostasyn.c +++ b/Utilities/cmcurl/lib/hostasyn.c @@ -78,7 +78,7 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns = Curl_cache_addr(data, ai, - data->state.async.hostname, + data->state.async.hostname, 0, data->state.async.port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 9738806..d0dc2e8 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -167,18 +167,25 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, /* * Create a hostcache id string for the provided host + port, to be used by - * the DNS caching. Without alloc. + * the DNS caching. Without alloc. Return length of the id string. */ -static void -create_hostcache_id(const char *name, int port, char *ptr, size_t buflen) +static size_t +create_hostcache_id(const char *name, + size_t nlen, /* 0 or actual name length */ + int port, char *ptr, size_t buflen) { - size_t len = strlen(name); + size_t len = nlen ? nlen : strlen(name); + size_t olen = 0; + DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); if(len > (buflen - 7)) len = buflen - 7; /* store and lower case the name */ - while(len--) + while(len--) { *ptr++ = Curl_raw_tolower(*name++); - msnprintf(ptr, 7, ":%u", port); + olen++; + } + olen += msnprintf(ptr, 7, ":%u", port); + return olen; } struct hostcache_prune_data { @@ -260,20 +267,18 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, int port) { struct Curl_dns_entry *dns = NULL; - size_t entry_len; char entry_id[MAX_HOSTCACHE_LEN]; /* Create an entry id, based upon the hostname and port */ - create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); + size_t entry_len = create_hostcache_id(hostname, 0, port, + entry_id, sizeof(entry_id)); /* See if its already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); /* No entry found in cache, check if we might have a wildcard entry */ if(!dns && data->state.wildcard_resolve) { - create_hostcache_id("*", port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); + entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id)); /* See if it's already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); @@ -438,6 +443,7 @@ struct Curl_dns_entry * Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, const char *hostname, + size_t hostlen, /* length or zero */ int port) { char entry_id[MAX_HOSTCACHE_LEN]; @@ -461,8 +467,8 @@ Curl_cache_addr(struct Curl_easy *data, } /* Create an entry id, based upon the hostname and port */ - create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); + entry_len = create_hostcache_id(hostname, hostlen, port, + entry_id, sizeof(entry_id)); dns->inuse = 1; /* the cache has the first reference */ dns->addr = addr; /* this is the address(es) */ @@ -791,7 +797,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* we got a response, store it in the cache */ - dns = Curl_cache_addr(data, addr, hostname, port); + dns = Curl_cache_addr(data, addr, hostname, 0, port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -1059,8 +1065,7 @@ void Curl_hostcache_clean(struct Curl_easy *data, CURLcode Curl_loadhostpairs(struct Curl_easy *data) { struct curl_slist *hostp; - char hostname[256]; - int port = 0; + char *host_end; /* Default is no wildcard found */ data->state.wildcard_resolve = false; @@ -1070,18 +1075,25 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(!hostp->data) continue; if(hostp->data[0] == '-') { + unsigned long num = 0; size_t entry_len; - - if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) { - infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'", + size_t hlen = 0; + host_end = strchr(&hostp->data[1], ':'); + + if(host_end) { + hlen = host_end - &hostp->data[1]; + num = strtoul(++host_end, NULL, 10); + if(!hlen || (num > 0xffff)) + host_end = NULL; + } + if(!host_end) { + infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'", hostp->data); continue; } - /* Create an entry id, based upon the hostname and port */ - create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); - + entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num, + entry_id, sizeof(entry_id)); if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); @@ -1102,25 +1114,22 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) char *addr_begin; char *addr_end; char *port_ptr; + int port = 0; char *end_ptr; bool permanent = TRUE; - char *host_begin; - char *host_end; unsigned long tmp_port; bool error = true; + char *host_begin = hostp->data; + size_t hlen = 0; - host_begin = hostp->data; if(host_begin[0] == '+') { host_begin++; permanent = FALSE; } host_end = strchr(host_begin, ':'); - if(!host_end || - ((host_end - host_begin) >= (ptrdiff_t)sizeof(hostname))) + if(!host_end) goto err; - - memcpy(hostname, host_begin, host_end - host_begin); - hostname[host_end - host_begin] = '\0'; + hlen = host_end - host_begin; port_ptr = host_end + 1; tmp_port = strtoul(port_ptr, &end_ptr, 10); @@ -1196,8 +1205,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) } /* Create an entry id, based upon the hostname and port */ - create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); + entry_len = create_hostcache_id(host_begin, hlen, port, + entry_id, sizeof(entry_id)); if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); @@ -1206,8 +1215,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); if(dns) { - infof(data, "RESOLVE %s:%d is - old addresses discarded", - hostname, port); + infof(data, "RESOLVE %.*s:%d is - old addresses discarded", + (int)hlen, host_begin, port); /* delete old entry, there are two reasons for this 1. old entry may have different addresses. 2. even if entry with correct addresses is already in the cache, @@ -1223,7 +1232,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) } /* put this new host in the cache */ - dns = Curl_cache_addr(data, head, hostname, port); + dns = Curl_cache_addr(data, head, host_begin, hlen, port); if(dns) { if(permanent) dns->timestamp = 0; /* mark as permanent */ @@ -1239,13 +1248,13 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) Curl_freeaddrinfo(head); return CURLE_OUT_OF_MEMORY; } - infof(data, "Added %s:%d:%s to DNS cache%s", - hostname, port, addresses, permanent ? "" : " (non-permanent)"); + infof(data, "Added %.*s:%d:%s to DNS cache%s", + (int)hlen, host_begin, port, addresses, + permanent ? "" : " (non-permanent)"); /* Wildcard hostname */ - if(hostname[0] == '*' && hostname[1] == '\0') { - infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks", - hostname, port); + if((hlen == 1) && (host_begin[0] == '*')) { + infof(data, "RESOLVE *:%d using wildcard", port); data->state.wildcard_resolve = true; } } diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index e0d13cd..4b5481f 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h @@ -178,7 +178,7 @@ Curl_fetch_addr(struct Curl_easy *data, */ struct Curl_dns_entry * Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, - const char *hostname, int port); + const char *hostname, size_t hostlen, int port); #ifndef INADDR_NONE #define CURL_INADDR_NONE (in_addr_t) ~0 diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index cb585e7..faa486c 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -88,6 +88,7 @@ #include "hsts.h" #include "ws.h" #include "c-hyper.h" +#include "curl_ctype.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -233,15 +234,12 @@ static CURLcode http_setup_conn(struct Curl_easy *data, Curl_mime_initpart(&http->form); data->req.p.http = http; + connkeep(conn, "HTTP default"); - if((data->state.httpwant == CURL_HTTP_VERSION_3) - || (data->state.httpwant == CURL_HTTP_VERSION_3ONLY)) { + if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { CURLcode result = Curl_conn_may_http3(data, conn); if(result) return result; - - /* TODO: HTTP lower version eyeballing */ - conn->transport = TRNSPRT_QUIC; } return CURLE_OK; @@ -2342,7 +2340,16 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; } - if(http->postsize) { + /* For really small puts we don't use Expect: headers at all, and for + the somewhat bigger ones we allow the app to disable it. Just make + sure that the expect100header is always set to the preferred value + here. */ + ptr = Curl_checkheaders(data, STRCONST("Expect")); + if(ptr) { + data->state.expect100header = + Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); + } + else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { result = expect100(data, conn, r); if(result) return result; @@ -4155,11 +4162,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(!k->headerline++) { /* This is the first header, it MUST be the error code line or else we consider this to be the body right away! */ - int httpversion_major; - int rtspversion_major; - int nc = 0; -#define HEADER1 headp /* no conversion needed, just use headp */ - + bool fine_statusline = FALSE; if(conn->handler->protocol & PROTO_FAMILY_HTTP) { /* * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 @@ -4168,39 +4171,60 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * says. We allow any three-digit number here, but we cannot make * guarantees on future behaviors since it isn't within the protocol. */ - char separator; - char twoorthree[2]; int httpversion = 0; - char digit4 = 0; - nc = sscanf(HEADER1, - " HTTP/%1d.%1d%c%3d%c", - &httpversion_major, - &httpversion, - &separator, - &k->httpcode, - &digit4); - - if(nc == 1 && httpversion_major >= 2 && - 2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) { - conn->httpversion = 0; - nc = 4; - separator = ' '; - } - - /* There can only be a 4th response code digit stored in 'digit4' if - all the other fields were parsed and stored first, so nc is 5 when - digit4 a digit. - - The sscanf() line above will also allow zero-prefixed and negative - numbers, so we check for that too here. - */ - else if(ISDIGIT(digit4) || (nc >= 4 && k->httpcode < 100)) { - failf(data, "Unsupported response code in HTTP response"); - return CURLE_UNSUPPORTED_PROTOCOL; + char *p = headp; + + while(*p && ISBLANK(*p)) + p++; + if(!strncmp(p, "HTTP/", 5)) { + p += 5; + switch(*p) { + case '1': + p++; + if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) { + if(ISBLANK(p[2])) { + httpversion = 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 + + (p[2] - '0'); + p += 3; + if(ISSPACE(*p)) + fine_statusline = TRUE; + } + } + } + if(!fine_statusline) { + failf(data, "Unsupported HTTP/1 subversion in response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + break; + case '2': + case '3': + if(!ISBLANK(p[1])) + break; + httpversion = (*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 + + (p[2] - '0'); + p += 3; + if(!ISSPACE(*p)) + break; + fine_statusline = TRUE; + } + break; + default: /* unsupported */ + failf(data, "Unsupported HTTP version in response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } } - if((nc >= 4) && (' ' == separator)) { - httpversion += 10 * httpversion_major; + if(fine_statusline) { + if(k->httpcode < 100) { + failf(data, "Unsupported response code in HTTP response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } switch(httpversion) { case 10: case 11: @@ -4227,51 +4251,50 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; } } - else if(!nc) { - /* this is the real world, not a Nirvana - NCSA 1.5.x returns this crap when asked for HTTP/1.1 - */ - nc = sscanf(HEADER1, " HTTP %3d", &k->httpcode); - conn->httpversion = 10; - + else { /* If user has set option HTTP200ALIASES, compare header line against list of aliases */ - if(!nc) { - statusline check = - checkhttpprefix(data, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - if(check == STATUS_DONE) { - nc = 1; - k->httpcode = 200; - conn->httpversion = 10; - } + statusline check = + checkhttpprefix(data, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + if(check == STATUS_DONE) { + fine_statusline = TRUE; + k->httpcode = 200; + conn->httpversion = 10; } } - else { - failf(data, "Unsupported HTTP version in response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } } else if(conn->handler->protocol & CURLPROTO_RTSP) { - char separator; - int rtspversion; - nc = sscanf(HEADER1, - " RTSP/%1d.%1d%c%3d", - &rtspversion_major, - &rtspversion, - &separator, - &k->httpcode); - if((nc == 4) && (' ' == separator)) { - conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */ - } - else { - nc = 0; + char *p = headp; + while(*p && ISBLANK(*p)) + p++; + if(!strncmp(p, "RTSP/", 5)) { + p += 5; + if(ISDIGIT(*p)) { + p++; + if((p[0] == '.') && ISDIGIT(p[1])) { + if(ISBLANK(p[2])) { + p += 3; + if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { + k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + (p[2] - '0'); + p += 3; + if(ISSPACE(*p)) { + fine_statusline = TRUE; + conn->httpversion = 11; /* RTSP acts like HTTP 1.1 */ + } + } + } + } + } + if(!fine_statusline) + return CURLE_WEIRD_SERVER_REPLY; } } - if(nc) { + if(fine_statusline) { result = Curl_http_statusline(data, conn); if(result) return result; diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index bdb5e73..b0ce87d 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -98,7 +98,6 @@ static size_t populate_binsettings(uint8_t *binsettings, struct cf_h2_ctx { nghttp2_session *h2; uint32_t max_concurrent_streams; - bool enable_push; /* The easy handle used in the current filter call, cleared at return */ struct cf_call_data call_data; @@ -116,6 +115,10 @@ struct cf_h2_ctx { int32_t pause_stream_id; /* stream ID which paused nghttp2_session_mem_recv */ size_t drain_total; /* sum of all stream's UrlState.drain */ + int32_t goaway_error; + int32_t last_stream_id; + BIT(goaway); + BIT(enable_push); }; /* How to access `call_data` from a cf_h2 filter */ @@ -364,41 +367,42 @@ static void http2_stream_free(struct HTTP *stream) } /* + * Returns nonzero if current HTTP/2 session should be closed. + */ +static int should_close_session(struct cf_h2_ctx *ctx) +{ + return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) && + !nghttp2_session_want_write(ctx->h2); +} + +/* * The server may send us data at any point (e.g. PING frames). Therefore, * we cannot assume that an HTTP/2 socket is dead just because it is readable. * * Check the lower filters first and, if successful, peek at the socket * and distinguish between closed and data. */ -static bool http2_connisdead(struct Curl_cfilter *cf, struct Curl_easy *data) +static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, + bool *input_pending) { struct cf_h2_ctx *ctx = cf->ctx; - int sval; - bool dead = TRUE; + bool alive = TRUE; - if(!cf->next || !cf->next->cft->is_alive(cf->next, data)) - return TRUE; + *input_pending = FALSE; + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + return FALSE; - sval = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0); - if(sval == 0) { - /* timeout */ - dead = FALSE; - } - else if(sval & CURL_CSELECT_ERR) { - /* socket is in an error state */ - dead = TRUE; - } - else if(sval & CURL_CSELECT_IN) { + if(*input_pending) { /* This happens before we've sent off a request and the connection is not in use by any other transfer, there shouldn't be any data here, only "protocol frames" */ CURLcode result; ssize_t nread = -1; + *input_pending = FALSE; Curl_attach_connection(data, cf->conn); nread = Curl_conn_cf_recv(cf->next, data, ctx->inbuf, H2_BUFSIZE, &result); - dead = FALSE; if(nread != -1) { DEBUGF(LOG_CF(data, cf, "%d bytes stray data read before trying " "h2 connection", (int)nread)); @@ -406,15 +410,19 @@ static bool http2_connisdead(struct Curl_cfilter *cf, struct Curl_easy *data) ctx->inbuflen = nread; if(h2_process_pending_input(cf, data, &result) < 0) /* immediate error, considered dead */ - dead = TRUE; + alive = FALSE; + else { + alive = !should_close_session(ctx); + } } - else + else { /* the read failed so let's say this is dead anyway */ - dead = TRUE; + alive = FALSE; + } Curl_detach_connection(data); } - return dead; + return alive; } static CURLcode http2_send_ping(struct Curl_cfilter *cf, @@ -815,7 +823,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); ctx->enable_push = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_ENABLE_PUSH); + session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS == %d", ctx->max_concurrent_streams)); DEBUGF(LOG_CF(data, cf, "ENABLE_PUSH == %s", @@ -829,9 +837,12 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, break; } case NGHTTP2_GOAWAY: + ctx->goaway = TRUE; + ctx->goaway_error = frame->goaway.error_code; + ctx->last_stream_id = frame->goaway.last_stream_id; if(data) { infof(data, "recveived GOAWAY, error=%d, last_stream=%u", - frame->goaway.error_code, frame->goaway.last_stream_id); + ctx->goaway_error, ctx->last_stream_id); multi_connchanged(data->multi); } break; @@ -858,7 +869,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, switch(frame->hd.type) { case NGHTTP2_DATA: - /* If body started on this stream, then receiving DATA is illegal. */ + /* If !body started on this stream, then receiving DATA is illegal. */ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame DATA", stream_id)); if(!stream->bodystarted) { rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, @@ -940,7 +951,21 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, break; case NGHTTP2_RST_STREAM: DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv RST", stream_id)); + stream->closed = TRUE; stream->reset = TRUE; + drain_this(cf, data); + Curl_expire(data, 0, EXPIRE_RUN_NOW); + break; + case NGHTTP2_WINDOW_UPDATE: + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv WINDOW_UPDATE", stream_id)); + if((data_s->req.keepon & KEEP_SEND_HOLD) && + (data_s->req.keepon & KEEP_SEND)) { + data_s->req.keepon &= ~KEEP_SEND_HOLD; + drain_this(cf, data_s); + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] un-holding after win update", + stream_id)); + } break; default: DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame %x", @@ -1006,18 +1031,6 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, return NGHTTP2_ERR_PAUSE; } -#if 0 - /* pause execution of nghttp2 if we received data for another handle - in order to process them first. */ - if(CF_DATA_CURRENT(cf) != data_s) { - ctx->pause_stream_id = stream_id; - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] not call_data -> NGHTTP2_ERR_PAUSE", - stream_id)); - drain_this(cf, data_s); - return NGHTTP2_ERR_PAUSE; - } -#endif - return 0; } @@ -1030,44 +1043,43 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, struct HTTP *stream; int rv; (void)session; - (void)stream_id; - if(stream_id) { - /* get the stream from the hash based on Stream ID, stream ID zero is for - connection-oriented stuff */ - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) { - /* We could get stream ID not in the hash. For example, if we - decided to reject stream (e.g., PUSH_PROMISE). */ - return 0; - } - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] on_stream_close(), %s (err %d)", - stream_id, nghttp2_http2_strerror(error_code), error_code)); - stream = data_s->req.p.http; - if(!stream) - return NGHTTP2_ERR_CALLBACK_FAILURE; + /* get the stream from the hash based on Stream ID, stream ID zero is for + connection-oriented stuff */ + data_s = stream_id? + nghttp2_session_get_stream_user_data(session, stream_id) : NULL; + if(!data_s) { + return 0; + } + stream = data_s->req.p.http; + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] on_stream_close(), %s (err %d)", + stream_id, nghttp2_http2_strerror(error_code), error_code)); + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; - stream->closed = TRUE; - if(CF_DATA_CURRENT(cf) != data_s) { - drain_this(cf, data_s); - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - } - stream->error = error_code; + stream->closed = TRUE; + stream->error = error_code; + if(stream->error) + stream->reset = TRUE; - /* remove the entry from the hash as the stream is now gone */ - rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); - if(rv) { - infof(data_s, "http/2: failed to clear user_data for stream %u", - stream_id); - DEBUGASSERT(0); - } - if(stream_id == ctx->pause_stream_id) { - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed the pause stream", - stream_id)); - ctx->pause_stream_id = 0; - } - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed, cleared", stream_id)); + if(CF_DATA_CURRENT(cf) != data_s) { + drain_this(cf, data_s); + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); } + + /* remove `data_s` from the nghttp2 stream */ + rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); + if(rv) { + infof(data_s, "http/2: failed to clear user_data for stream %u", + stream_id); + DEBUGASSERT(0); + } + if(stream_id == ctx->pause_stream_id) { + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed the pause stream", + stream_id)); + ctx->pause_stream_id = 0; + } + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed now", stream_id)); return 0; } @@ -1383,7 +1395,8 @@ static void http2_data_done(struct Curl_cfilter *cf, ctx->pause_stream_id = 0; } - if(premature || (!stream->closed && stream->stream_id)) { + (void)premature; + if(!stream->closed && stream->stream_id) { /* RST_STREAM */ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] RST", stream->stream_id)); if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1446,15 +1459,6 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, } /* - * Returns nonzero if current HTTP/2 session should be closed. - */ -static int should_close_session(struct cf_h2_ctx *ctx) -{ - return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) && - !nghttp2_session_want_write(ctx->h2); -} - -/* * h2_process_pending_input() processes pending input left in * httpc->inbuf. Then, call h2_session_send() to send pending data. * This function returns 0 if it succeeds, or -1 and error code will @@ -1586,8 +1590,6 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, } } - /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ - stream->closed = FALSE; if(stream->error == NGHTTP2_REFUSED_STREAM) { DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new " "connection", stream->stream_id)); @@ -1603,6 +1605,11 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, *err = CURLE_HTTP2_STREAM; return -1; } + else if(stream->reset) { + failf(data, "HTTP/2 stream %u was reset", stream->stream_id); + *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; + return -1; + } if(!stream->bodystarted) { failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " @@ -1638,7 +1645,7 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, stream->close_handled = TRUE; - DEBUGF(LOG_CF(data, cf, "http2_recv returns 0, http2_handle_stream_close")); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] closed cleanly", stream->stream_id)); return 0; } @@ -1720,9 +1727,29 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, struct HTTP *stream = data->req.p.http; ssize_t nread = -1; struct cf_call_data save; + bool conn_is_closed = FALSE; CF_DATA_SAVE(save, cf, data); + /* If the h2 session has told us to GOAWAY with an error AND + * indicated the highest stream id it has processes AND + * the stream we are trying to read has a higher id, this + * means we will most likely not receive any more for it. + * Treat this as if the server explicitly had RST the stream */ + if((ctx->goaway && ctx->goaway_error && + ctx->last_stream_id > 0 && + ctx->last_stream_id < stream->stream_id)) { + stream->reset = TRUE; + } + + /* If a stream is RST, it does not matter what state the h2 session + * is in, our answer to receiving data is always the same. */ + if(stream->reset) { + *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; + nread = -1; + goto out; + } + if(should_close_session(ctx)) { DEBUGF(LOG_CF(data, cf, "http2_recv: nothing to do in this session")); if(cf->conn->bits.close) { @@ -1763,7 +1790,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: win %u/%u", + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv: win %u/%u", stream->stream_id, nghttp2_session_get_local_window_size(ctx->h2), nghttp2_session_get_stream_local_window_size(ctx->h2, @@ -1846,57 +1873,40 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, stream->memlen = 0; if(ctx->inbuflen > 0) { - DEBUGF(LOG_CF(data, cf, "Use data left in connection buffer, nread=%zd", - ctx->inbuflen - ctx->nread_inbuf)); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] %zd bytes in inbuf", + stream->stream_id, ctx->inbuflen - ctx->nread_inbuf)); if(h2_process_pending_input(cf, data, err)) return -1; } - while(stream->memlen == 0 /* have no data for this stream */ - && !ctx->pause_stream_id /* we are not paused either */ - && ctx->inbuflen == 0) { /* and out input buffer is empty */ + while(stream->memlen == 0 && /* have no data for this stream */ + !stream->closed && /* and it is not closed/reset */ + !ctx->pause_stream_id && /* we are not paused either */ + ctx->inbuflen == 0 && /* and out input buffer is empty */ + !conn_is_closed) { /* and connection is not closed */ /* Receive data from the "lower" filters */ nread = Curl_conn_cf_recv(cf->next, data, ctx->inbuf, H2_BUFSIZE, err); if(nread < 0) { - if(*err != CURLE_AGAIN) - failf(data, "Failed receiving HTTP2 data"); - else if(stream->closed) { - /* received when the stream was already closed! */ - nread = http2_handle_stream_close(cf, data, stream, err); - goto out; + DEBUGASSERT(*err); + if(*err == CURLE_AGAIN) { + break; } - - /* nothing to read from the lower layers, clear drain */ - drained_transfer(cf, data); - nread = -1; - goto out; + failf(data, "Failed receiving HTTP2 data"); + conn_is_closed = TRUE; } else if(nread == 0) { - if(!stream->closed) { - /* This will happen when the server or proxy server is SIGKILLed - during data transfer. We should emit an error since our data - received may be incomplete. */ - failf(data, "HTTP/2 stream %u was not closed cleanly before" - " end of the underlying stream", - stream->stream_id); - drained_transfer(cf, data); - *err = CURLE_PARTIAL_FILE; - nread = -1; - goto out; - } - - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] end of stream", + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] underlying connection is closed", stream->stream_id)); - *err = CURLE_OK; - nread = 0; - goto out; + conn_is_closed = TRUE; + } + else { + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] read %zd from connection", + stream->stream_id, nread)); + ctx->inbuflen = nread; + DEBUGASSERT(ctx->nread_inbuf == 0); + if(h2_process_pending_input(cf, data, err)) + return -1; } - - DEBUGF(LOG_CF(data, cf, "read %zd from connection", nread)); - ctx->inbuflen = nread; - DEBUGASSERT(ctx->nread_inbuf == 0); - if(h2_process_pending_input(cf, data, err)) - return -1; } } @@ -1933,11 +1943,18 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, *err = CURLE_OK; nread = retlen; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_h2_recv -> %zd", - stream->stream_id, nread)); goto out; } + if(conn_is_closed && !stream->closed) { + /* underlying connection is closed and we have nothing for the stream. + * Treat this as a RST */ + stream->closed = stream->reset = TRUE; + failf(data, "HTTP/2 stream %u was not closed cleanly before" + " end of the underlying connection", + stream->stream_id); + } + if(stream->closed) { nread = http2_handle_stream_close(cf, data, stream, err); goto out; @@ -1950,9 +1967,9 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } *err = CURLE_AGAIN; nread = -1; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv -> AGAIN", - stream->stream_id)); out: + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv -> %zd, %d", + stream->stream_id, nread, *err)); CF_DATA_RESTORE(cf, save); return nread; } @@ -1976,19 +1993,20 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result; struct h2h3req *hreq; struct cf_call_data save; + ssize_t nwritten; CF_DATA_SAVE(save, cf, data); - DEBUGF(LOG_CF(data, cf, "send len=%zu", len)); + DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) start", len)); if(stream->stream_id != -1) { if(stream->close_handled) { infof(data, "stream %u closed", stream->stream_id); *err = CURLE_HTTP2_STREAM; - len = -1; + nwritten = -1; goto out; } else if(stream->closed) { - len = http2_handle_stream_close(cf, data, stream, err); + nwritten = http2_handle_stream_close(cf, data, stream, err); goto out; } /* If stream_id != -1, we have dispatched request HEADERS, and now @@ -1998,26 +2016,24 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, rv = nghttp2_session_resume_data(ctx->h2, stream->stream_id); if(nghttp2_is_fatal(rv)) { *err = CURLE_SEND_ERROR; - len = -1; + nwritten = -1; goto out; } result = h2_session_send(cf, data); if(result) { *err = result; - len = -1; + nwritten = -1; goto out; } - len -= stream->upload_len; - /* Nullify here because we call nghttp2_session_send() and they - might refer to the old buffer. */ + nwritten = (ssize_t)len - (ssize_t)stream->upload_len; stream->upload_mem = NULL; stream->upload_len = 0; if(should_close_session(ctx)) { DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); *err = CURLE_HTTP2; - len = -1; + nwritten = -1; goto out; } @@ -2029,26 +2045,36 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, nghttp2_session_resume_data(ctx->h2, stream->stream_id); } -#ifdef DEBUG_HTTP2 - if(!len) { - infof(data, "http2_send: easy %p (stream %u) win %u/%u", - data, stream->stream_id, - nghttp2_session_get_remote_window_size(ctx->h2), - nghttp2_session_get_stream_remote_window_size(ctx->h2, - stream->stream_id) - ); - + if(!nwritten) { + size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, + stream->stream_id); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_send: win %u/%zu", + stream->stream_id, + nghttp2_session_get_remote_window_size(ctx->h2), rwin)); + if(rwin == 0) { + /* We cannot upload more as the stream's remote window size + * is 0. We need to receive WIN_UPDATEs before we can continue. + */ + data->req.keepon |= KEEP_SEND_HOLD; + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] holding send as remote flow " + "window is exhausted", stream->stream_id)); + } } - infof(data, "http2_send returns %zu for stream %u", len, - stream->stream_id); -#endif + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_send returns %zd ", + stream->stream_id, nwritten)); + + /* handled writing BODY for open stream. */ goto out; } - + /* Stream has not been opened yet. `buf` is expected to contain + * request headers. */ + /* TODO: this assumes that the `buf` and `len` we are called with + * is *all* HEADERs and no body. We have no way to determine here + * if that is indeed the case. */ result = Curl_pseudo_headers(data, buf, len, NULL, &hreq); if(result) { *err = result; - len = -1; + nwritten = -1; goto out; } nheader = hreq->entries; @@ -2057,7 +2083,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(!nva) { Curl_pseudo_free(hreq); *err = CURLE_OUT_OF_MEMORY; - len = -1; + nwritten = -1; goto out; } else { @@ -2104,25 +2130,28 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", nghttp2_strerror(stream_id), stream_id)); *err = CURLE_SEND_ERROR; - len = -1; + nwritten = -1; goto out; } infof(data, "Using Stream ID: %u (easy handle %p)", stream_id, (void *)data); stream->stream_id = stream_id; + /* See TODO above. We assume that the whole buf was consumed by + * generating the request headers. */ + nwritten = len; result = h2_session_send(cf, data); if(result) { *err = result; - len = -1; + nwritten = -1; goto out; } if(should_close_session(ctx)) { DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); *err = CURLE_HTTP2; - len = -1; + nwritten = -1; goto out; } @@ -2137,7 +2166,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, out: CF_DATA_RESTORE(cf, save); - return len; + return nwritten; } static int cf_h2_get_select_socks(struct Curl_cfilter *cf, @@ -2160,7 +2189,7 @@ static int cf_h2_get_select_socks(struct Curl_cfilter *cf, /* we're (still uploading OR the HTTP/2 layer wants to send data) AND there's a window to send data in */ - if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) || + if((((k->keepon & KEEP_SENDBITS) == KEEP_SEND) || nghttp2_session_want_write(ctx->h2)) && (nghttp2_session_get_remote_window_size(ctx->h2) && nghttp2_session_get_stream_remote_window_size(ctx->h2, @@ -2329,14 +2358,17 @@ static bool cf_h2_data_pending(struct Curl_cfilter *cf, } static bool cf_h2_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + bool *input_pending) { struct cf_h2_ctx *ctx = cf->ctx; CURLcode result; struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - result = (ctx && ctx->h2 && !http2_connisdead(cf, data)); + result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending)); + DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d", + result, *input_pending)); CF_DATA_RESTORE(cf, save); return result; } @@ -2479,7 +2511,8 @@ bool Curl_http2_may_switch(struct Curl_easy *data, int sockindex) { (void)sockindex; - if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { + if(!Curl_conn_is_http2(data, conn, sockindex) && + data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { #ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { /* We don't support HTTP/2 proxies yet. Also it's debatable diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index f8ce169..7d50cff 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c @@ -58,13 +58,15 @@ #define TIMESTAMP_SIZE 17 -static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l) +/* hex-encoded with trailing null */ +#define SHA256_HEX_LENGTH (2 * SHA256_DIGEST_LENGTH + 1) + +static void sha256_to_hex(char *dst, unsigned char *sha) { int i; - DEBUGASSERT(dst_l >= 65); - for(i = 0; i < 32; ++i) { - msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]); + for(i = 0; i < SHA256_DIGEST_LENGTH; ++i) { + msnprintf(dst + (i * 2), SHA256_HEX_LENGTH - (i * 2), "%02x", sha[i]); } } @@ -135,6 +137,7 @@ static CURLcode make_headers(struct Curl_easy *data, char *timestamp, char *provider1, char **date_header, + char *content_sha256_header, struct dynbuf *canonical_headers, struct dynbuf *signed_headers) { @@ -189,6 +192,13 @@ static CURLcode make_headers(struct Curl_easy *data, } + if (*content_sha256_header) { + tmp_head = curl_slist_append(head, content_sha256_header); + if(!tmp_head) + goto fail; + head = tmp_head; + } + for(l = data->set.headers; l; l = l->next) { tmp_head = curl_slist_append(head, l->data); if(!tmp_head) @@ -267,6 +277,9 @@ fail: } #define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256")) +/* add 2 for ": " between header name and value */ +#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + \ + SHA256_HEX_LENGTH) /* try to parse a payload hash from the content-sha256 header */ static char *parse_content_sha_hdr(struct Curl_easy *data, @@ -300,6 +313,63 @@ static char *parse_content_sha_hdr(struct Curl_easy *data, return value; } +static CURLcode calc_payload_hash(struct Curl_easy *data, + unsigned char *sha_hash, char *sha_hex) +{ + const char *post_data = data->set.postfields; + size_t post_data_len = 0; + CURLcode result; + + if(post_data) { + if(data->set.postfieldsize < 0) + post_data_len = strlen(post_data); + else + post_data_len = (size_t)data->set.postfieldsize; + } + result = Curl_sha256it(sha_hash, (const unsigned char *) post_data, + post_data_len); + if(!result) + sha256_to_hex(sha_hex, sha_hash); + return result; +} + +#define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD" + +static CURLcode calc_s3_payload_hash(struct Curl_easy *data, + Curl_HttpReq httpreq, char *provider1, + unsigned char *sha_hash, + char *sha_hex, char *header) +{ + bool empty_method = (httpreq == HTTPREQ_GET || httpreq == HTTPREQ_HEAD); + /* The request method or filesize indicate no request payload */ + bool empty_payload = (empty_method || data->set.filesize == 0); + /* The POST payload is in memory */ + bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields); + CURLcode ret = CURLE_OUT_OF_MEMORY; + + if(empty_payload || post_payload) { + /* Calculate a real hash when we know the request payload */ + ret = calc_payload_hash(data, sha_hash, sha_hex); + if(ret) + goto fail; + } + else { + /* Fall back to s3's UNSIGNED-PAYLOAD */ + size_t len = sizeof(S3_UNSIGNED_PAYLOAD) - 1; + DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */ + memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len); + sha_hex[len] = 0; + } + + /* format the required content-sha256 header */ + msnprintf(header, CONTENT_SHA256_HDR_LEN, + "x-%s-content-sha256: %s", provider1, sha_hex); + + ret = CURLE_OK; +fail: + return ret; +} + CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) { CURLcode ret = CURLE_OUT_OF_MEMORY; @@ -310,6 +380,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) char provider1[MAX_SIGV4_LEN + 1]=""; char region[MAX_SIGV4_LEN + 1]=""; char service[MAX_SIGV4_LEN + 1]=""; + bool sign_as_s3 = false; const char *hostname = conn->host.name; time_t clock; struct tm tm; @@ -318,20 +389,21 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) struct dynbuf canonical_headers; struct dynbuf signed_headers; char *date_header = NULL; + Curl_HttpReq httpreq; + const char *method = NULL; char *payload_hash = NULL; size_t payload_hash_len = 0; - const char *post_data = data->set.postfields; - size_t post_data_len = 0; - unsigned char sha_hash[32]; - char sha_hex[65]; + unsigned char sha_hash[SHA256_DIGEST_LENGTH]; + char sha_hex[SHA256_HEX_LENGTH]; + char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ char *canonical_request = NULL; char *request_type = NULL; char *credential_scope = NULL; char *str_to_sign = NULL; const char *user = data->state.aptr.user ? data->state.aptr.user : ""; char *secret = NULL; - unsigned char sign0[32] = {0}; - unsigned char sign1[32] = {0}; + unsigned char sign0[SHA256_DIGEST_LENGTH] = {0}; + unsigned char sign1[SHA256_DIGEST_LENGTH] = {0}; char *auth_headers = NULL; DEBUGASSERT(!proxy); @@ -408,6 +480,29 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) } } + 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); + + if(!payload_hash) { + if(sign_as_s3) + ret = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, + sha_hex, content_sha256_hdr); + else + ret = calc_payload_hash(data, sha_hash, sha_hex); + if(ret) + goto fail; + + payload_hash = sha_hex; + /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */ + payload_hash_len = strlen(sha_hex); + } + #ifdef DEBUGBUILD { char *force_timestamp = getenv("CURL_FORCETIME"); @@ -429,54 +524,37 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) } ret = make_headers(data, hostname, timestamp, provider1, - &date_header, &canonical_headers, &signed_headers); + &date_header, content_sha256_hdr, + &canonical_headers, &signed_headers); if(ret) goto fail; ret = CURLE_OUT_OF_MEMORY; + if(*content_sha256_hdr) { + /* make_headers() needed this without the \r\n for canonicalization */ + size_t hdrlen = strlen(content_sha256_hdr); + DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr)); + memcpy(content_sha256_hdr + hdrlen, "\r\n", 3); + } + memcpy(date, timestamp, sizeof(date)); date[sizeof(date) - 1] = 0; - payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len); - - if(!payload_hash) { - if(post_data) { - if(data->set.postfieldsize < 0) - post_data_len = strlen(post_data); - else - post_data_len = (size_t)data->set.postfieldsize; - } - if(Curl_sha256it(sha_hash, (const unsigned char *) post_data, - post_data_len)) - goto fail; - - sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex)); - payload_hash = sha_hex; - payload_hash_len = strlen(sha_hex); - } - - { - Curl_HttpReq httpreq; - const char *method; - - Curl_http_method(data, conn, &method, &httpreq); - - canonical_request = - curl_maprintf("%s\n" /* HTTPRequestMethod */ - "%s\n" /* CanonicalURI */ - "%s\n" /* CanonicalQueryString */ - "%s\n" /* CanonicalHeaders */ - "%s\n" /* SignedHeaders */ - "%.*s", /* HashedRequestPayload in hex */ - method, - data->state.up.path, - data->state.up.query ? data->state.up.query : "", - Curl_dyn_ptr(&canonical_headers), - Curl_dyn_ptr(&signed_headers), - (int)payload_hash_len, payload_hash); - if(!canonical_request) - goto fail; - } + canonical_request = + curl_maprintf("%s\n" /* HTTPRequestMethod */ + "%s\n" /* CanonicalURI */ + "%s\n" /* CanonicalQueryString */ + "%s\n" /* CanonicalHeaders */ + "%s\n" /* SignedHeaders */ + "%.*s", /* HashedRequestPayload in hex */ + method, + data->state.up.path, + data->state.up.query ? data->state.up.query : "", + Curl_dyn_ptr(&canonical_headers), + Curl_dyn_ptr(&signed_headers), + (int)payload_hash_len, payload_hash); + if(!canonical_request) + goto fail; /* provider 0 lowercase */ Curl_strntolower(provider0, provider0, strlen(provider0)); @@ -493,7 +571,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) strlen(canonical_request))) goto fail; - sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex)); + sha256_to_hex(sha_hex, sha_hash); /* provider 0 uppercase */ Curl_strntoupper(provider0, provider0, strlen(provider0)); @@ -527,20 +605,22 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); - sha256_to_hex(sha_hex, sign0, sizeof(sha_hex)); + sha256_to_hex(sha_hex, sign0); /* provider 0 uppercase */ auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 " "Credential=%s/%s, " "SignedHeaders=%s, " "Signature=%s\r\n" - "%s\r\n", + "%s\r\n" + "%s", /* optional sha256 header includes \r\n */ provider0, user, credential_scope, Curl_dyn_ptr(&signed_headers), sha_hex, - date_header); + date_header, + content_sha256_hdr); if(!auth_headers) { goto fail; } diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index fdd092d..9f214a3 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -403,7 +403,6 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, { CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - int subversion = 0; (void)cf; if((checkprefix("WWW-Authenticate:", header) && @@ -461,11 +460,14 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, STRCONST("Proxy-Connection:"), STRCONST("close"))) ts->close_connection = TRUE; - else if(2 == sscanf(header, "HTTP/1.%d %d", - &subversion, - &k->httpcode)) { + else if(!strncmp(header, "HTTP/1.", 7) && + ((header[7] == '0') || (header[7] == '1')) && + (header[8] == ' ') && + ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && + !ISDIGIT(header[12])) { /* store the HTTP code from the proxy */ - data->info.httpproxycode = k->httpcode; + data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 + + (header[10] - '0') * 10 + (header[11] - '0'); } return result; } diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index abba895..5f4b07e 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c @@ -184,6 +184,11 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host) if(!Curl_is_ASCII_name(host->name)) { char *decoded = idn_decode(host->name); if(decoded) { + if(!*decoded) { + /* zero length is a bad host name */ + Curl_idn_free(decoded); + return CURLE_URL_MALFORMAT; + } /* successful */ host->encalloc = decoded; /* change the name pointer to point to the encoded hostname */ diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c index 024f8da..770ed3a 100644 --- a/Utilities/cmcurl/lib/inet_ntop.c +++ b/Utilities/cmcurl/lib/inet_ntop.c @@ -42,6 +42,15 @@ #define INT16SZ 2 /* + * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * sure we have _some_ value for AF_INET6 without polluting our fake value + * everywhere. + */ +#if !defined(ENABLE_IPV6) && !defined(AF_INET6) +#define AF_INET6 (AF_INET + 1) +#endif + +/* * Format an IPv4 address, more or less like inet_ntop(). * * Returns `dst' (as a const) @@ -72,7 +81,6 @@ static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size) return dst; } -#ifdef ENABLE_IPV6 /* * Convert IPv6 binary address into presentation (printable) format. */ @@ -168,7 +176,6 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) strcpy(dst, tmp); return dst; } -#endif /* ENABLE_IPV6 */ /* * Convert a network format address to presentation format. @@ -187,10 +194,8 @@ char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) switch(af) { case AF_INET: return inet_ntop4((const unsigned char *)src, buf, size); -#ifdef ENABLE_IPV6 case AF_INET6: return inet_ntop6((const unsigned char *)src, buf, size); -#endif default: errno = EAFNOSUPPORT; return NULL; diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c index f2e17b8..7d3c698 100644 --- a/Utilities/cmcurl/lib/inet_pton.c +++ b/Utilities/cmcurl/lib/inet_pton.c @@ -39,14 +39,21 @@ #define INT16SZ 2 /* + * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * sure we have _some_ value for AF_INET6 without polluting our fake value + * everywhere. + */ +#if !defined(ENABLE_IPV6) && !defined(AF_INET6) +#define AF_INET6 (AF_INET + 1) +#endif + +/* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4(const char *src, unsigned char *dst); -#ifdef ENABLE_IPV6 static int inet_pton6(const char *src, unsigned char *dst); -#endif /* int * inet_pton(af, src, dst) @@ -70,10 +77,8 @@ Curl_inet_pton(int af, const char *src, void *dst) switch(af) { case AF_INET: return (inet_pton4(src, (unsigned char *)dst)); -#ifdef ENABLE_IPV6 case AF_INET6: return (inet_pton6(src, (unsigned char *)dst)); -#endif default: errno = EAFNOSUPPORT; return (-1); @@ -135,7 +140,6 @@ inet_pton4(const char *src, unsigned char *dst) return (1); } -#ifdef ENABLE_IPV6 /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. @@ -234,6 +238,5 @@ inet_pton6(const char *src, unsigned char *dst) memcpy(dst, tmp, IN6ADDRSZ); return (1); } -#endif /* ENABLE_IPV6 */ #endif /* HAVE_INET_PTON */ diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c index 3cd64e1..a71779a 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c @@ -721,8 +721,7 @@ int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, return 0; if(buf[3] != '-') - /* safe to ignore return code */ - (void)sscanf(buf, "%d", &ret_code); + ret_code = atoi(buf); if(buf[decoded_len - 1] == '\n') buf[decoded_len - 1] = '\0'; @@ -765,8 +764,9 @@ static int sec_set_protection_level(struct Curl_easy *data) pbsz = strstr(data->state.buffer, "PBSZ="); if(pbsz) { - /* ignore return code, use default value if it fails */ - (void)sscanf(pbsz, "PBSZ=%u", &buffer_size); + /* stick to default value if the check fails */ + if(!strncmp(pbsz, "PBSZ=", 5) && ISDIGIT(pbsz[5])) + buffer_size = atoi(&pbsz[5]); if(buffer_size < conn->buffer_size) conn->buffer_size = buffer_size; } diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 5e53f4c..595e4b3 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -140,6 +140,14 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp); #define ldap_err2string ldap_err2stringA #endif +#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1600) +/* Workaround for warning: + 'type cast' : conversion from 'int' to 'void *' of greater size */ +#undef LDAP_OPT_ON +#undef LDAP_OPT_OFF +#define LDAP_OPT_ON ((void *)(size_t)1) +#define LDAP_OPT_OFF ((void *)(size_t)0) +#endif static CURLcode ldap_do(struct Curl_easy *data, bool *done); diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index 0b54bc0..47af369 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c @@ -122,8 +122,9 @@ static CURLcode mqtt_send(struct Curl_easy *data, struct MQTT *mq = data->req.p.mqtt; ssize_t n; result = Curl_write(data, sockfd, buf, len, &n); - if(!result) - Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); + if(result) + return result; + Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); if(len != (size_t)n) { size_t nsend = len - n; char *sendleftovers = Curl_memdup(&buf[n], nsend); diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index f020a0b..731b259 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -445,9 +445,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ sockhash_destroy(&multi->sockhash); Curl_hash_destroy(&multi->hostcache); Curl_conncache_destroy(&multi->conn_cache); - Curl_llist_destroy(&multi->msglist, NULL); - Curl_llist_destroy(&multi->pending, NULL); - free(multi); return NULL; } @@ -459,6 +456,42 @@ struct Curl_multi *curl_multi_init(void) CURL_DNS_HASH_SIZE); } +static void link_easy(struct Curl_multi *multi, + struct Curl_easy *data) +{ + /* We add the new easy entry last in the list. */ + data->next = NULL; /* end of the line */ + if(multi->easyp) { + struct Curl_easy *last = multi->easylp; + last->next = data; + data->prev = last; + multi->easylp = data; /* the new last node */ + } + else { + /* first node, make prev NULL! */ + data->prev = NULL; + multi->easylp = multi->easyp = data; /* both first and last */ + } +} + +/* unlink the given easy handle from the linked list of easy handles */ +static void unlink_easy(struct Curl_multi *multi, + struct Curl_easy *data) +{ + /* make the previous node point to our next */ + if(data->prev) + data->prev->next = data->next; + else + multi->easyp = data->next; /* point to first node */ + + /* make our next point to our previous node */ + if(data->next) + data->next->prev = data->prev; + else + multi->easylp = data->prev; /* point to last node */ +} + + CURLMcode curl_multi_add_handle(struct Curl_multi *multi, struct Curl_easy *data) { @@ -554,19 +587,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->psl = &multi->psl; #endif - /* We add the new entry last in the list. */ - data->next = NULL; /* end of the line */ - if(multi->easyp) { - struct Curl_easy *last = multi->easylp; - last->next = data; - data->prev = last; - multi->easylp = data; /* the new last node */ - } - else { - /* first node, make prev NULL! */ - data->prev = NULL; - multi->easylp = multi->easyp = data; /* both first and last */ - } + link_easy(multi, data); /* increase the node-counter */ multi->num_easy++; @@ -841,10 +862,6 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, Curl_wildcard_dtor(&data->wildcard); - /* destroy the timeout list that is held in the easy handle, do this *after* - multi_done() as that may actually call Curl_expire that uses this */ - Curl_llist_destroy(&data->state.timeoutlist, NULL); - /* change state without using multistate(), only to make singlesocket() do what we want */ data->mstate = MSTATE_COMPLETED; @@ -917,17 +934,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, } } - /* make the previous node point to our next */ - if(data->prev) - data->prev->next = data->next; - else - multi->easyp = data->next; /* point to first node */ - - /* make our next point to our previous node */ - if(data->next) - data->next->prev = data->prev; - else - multi->easylp = data->prev; /* point to last node */ + unlink_easy(multi, data); /* NOTE NOTE NOTE We do not touch the easy handle here! */ @@ -976,7 +983,7 @@ void Curl_attach_connection(struct Curl_easy *data, data->conn = conn; Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data, &data->conn_queue); - if(conn->handler->attach) + if(conn->handler && conn->handler->attach) conn->handler->attach(data, conn); Curl_conn_ev_data_attach(conn, data); } @@ -2192,7 +2199,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_FTP /* some steps needed for wildcard matching */ if(data->state.wildcardmatch) { - struct WildcardData *wc = &data->wildcard; + struct WildcardData *wc = data->wildcard; if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { /* skip some states if it is important */ multi_done(data, CURLE_OK, FALSE); @@ -2344,7 +2351,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_FTP if(data->state.wildcardmatch && ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) { - data->wildcard.state = CURLWC_DONE; + data->wildcard->state = CURLWC_DONE; } #endif multistate(data, MSTATE_DONE); @@ -2574,7 +2581,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_FTP if(data->state.wildcardmatch) { - if(data->wildcard.state != CURLWC_DONE) { + if(data->wildcard->state != CURLWC_DONE) { /* if a wildcard is set and we are not ending -> lets start again with MSTATE_INIT */ multistate(data, MSTATE_INIT); @@ -2706,18 +2713,25 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) return CURLM_RECURSIVE_API_CALL; data = multi->easyp; - while(data) { + if(data) { CURLMcode result; + bool nosig = data->set.no_signal; SIGPIPE_VARIABLE(pipe_st); - sigpipe_ignore(data, &pipe_st); - result = multi_runsingle(multi, &now, data); + /* Do the loop and only alter the signal ignore state if the next handle + has a different NO_SIGNAL state than the previous */ + do { + if(data->set.no_signal != nosig) { + sigpipe_restore(&pipe_st); + sigpipe_ignore(data, &pipe_st); + nosig = data->set.no_signal; + } + result = multi_runsingle(multi, &now, data); + if(result) + returncode = result; + data = data->next; /* operate on next handle */ + } while(data); sigpipe_restore(&pipe_st); - - if(result) - returncode = result; - - data = data->next; /* operate on next handle */ } /* @@ -2788,9 +2802,6 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) sockhash_destroy(&multi->sockhash); Curl_conncache_destroy(&multi->conn_cache); - Curl_llist_destroy(&multi->msglist, NULL); - Curl_llist_destroy(&multi->pending, NULL); - Curl_hash_destroy(&multi->hostcache); Curl_psl_destroy(&multi->psl); diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c index 82ac1d8..1662dd3 100644 --- a/Utilities/cmcurl/lib/parsedate.c +++ b/Utilities/cmcurl/lib/parsedate.c @@ -212,56 +212,55 @@ static int checkday(const char *check, size_t len) { int i; const char * const *what; - bool found = FALSE; if(len > 3) what = &weekday[0]; - else + else if(len == 3) what = &Curl_wkday[0]; + else + return -1; /* too short */ for(i = 0; i<7; i++) { - if(strcasecompare(check, what[0])) { - found = TRUE; - break; - } + size_t ilen = strlen(what[0]); + if((ilen == len) && + strncasecompare(check, what[0], len)) + return i; what++; } - return found?i:-1; + return -1; } -static int checkmonth(const char *check) +static int checkmonth(const char *check, size_t len) { int i; - const char * const *what; - bool found = FALSE; + const char * const *what = &Curl_month[0]; + if(len != 3) + return -1; /* not a month */ - what = &Curl_month[0]; for(i = 0; i<12; i++) { - if(strcasecompare(check, what[0])) { - found = TRUE; - break; - } + if(strncasecompare(check, what[0], 3)) + return i; what++; } - return found?i:-1; /* return the offset or -1, no real offset is -1 */ + return -1; /* return the offset or -1, no real offset is -1 */ } /* return the time zone offset between GMT and the input one, in number of seconds or -1 if the timezone wasn't found/legal */ -static int checktz(const char *check) +static int checktz(const char *check, size_t len) { unsigned int i; - const struct tzinfo *what; - bool found = FALSE; + const struct tzinfo *what = tz; + if(len > 4) /* longer than any valid timezone */ + return -1; - what = tz; for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) { - if(strcasecompare(check, what->name)) { - found = TRUE; - break; - } + size_t ilen = strlen(what->name); + if((ilen == len) && + strncasecompare(check, what->name, len)) + return what->offset*60; what++; } - return found?what->offset*60:-1; + return -1; } static void skip(const char **date) @@ -294,6 +293,53 @@ static time_t time2epoch(int sec, int min, int hour, + hour) * 60 + min) * 60 + sec; } +/* Returns the value of a single-digit or two-digit decimal number, return + then pointer to after the number. The 'date' pointer is known to point to a + digit. */ +static int oneortwodigit(const char *date, const char **endp) +{ + int num = date[0] - '0'; + if(ISDIGIT(date[1])) { + *endp = &date[2]; + return num*10 + (date[1] - '0'); + } + *endp = &date[1]; + return num; +} + + +/* HH:MM:SS or HH:MM and accept single-digits too */ +static bool match_time(const char *date, + int *h, int *m, int *s, char **endp) +{ + const char *p; + int hh, mm, ss = 0; + hh = oneortwodigit(date, &p); + if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) { + mm = oneortwodigit(&p[1], &p); + if(mm < 60) { + if((*p == ':') && ISDIGIT(p[1])) { + ss = oneortwodigit(&p[1], &p); + if(ss <= 60) { + /* valid HH:MM:SS */ + goto match; + } + } + else { + /* valid HH:MM */ + goto match; + } + } + } + return FALSE; /* not a time string */ + match: + *h = hh; + *m = mm; + *s = ss; + *endp = (char *)p; + return TRUE; +} + /* * parsedate() * @@ -305,6 +351,9 @@ static time_t time2epoch(int sec, int min, int hour, * PARSEDATE_SOONER - time underflow at the low end of time_t */ +/* Wednesday is the longest name this parser knows about */ +#define NAME_LEN 12 + static int parsedate(const char *date, time_t *output) { time_t t = 0; @@ -327,32 +376,32 @@ static int parsedate(const char *date, time_t *output) if(ISALPHA(*date)) { /* a name coming up */ - char buf[32]=""; - size_t len; - if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz]", buf)) - len = strlen(buf); - else - len = 0; - - if(wdaynum == -1) { - wdaynum = checkday(buf, len); - if(wdaynum != -1) - found = TRUE; - } - if(!found && (monnum == -1)) { - monnum = checkmonth(buf); - if(monnum != -1) - found = TRUE; + size_t len = 0; + const char *p = date; + while(ISALPHA(*p) && (len < NAME_LEN)) { + p++; + len++; } - if(!found && (tzoff == -1)) { - /* this just must be a time zone string */ - tzoff = checktz(buf); - if(tzoff != -1) - found = TRUE; - } + if(len != NAME_LEN) { + if(wdaynum == -1) { + wdaynum = checkday(date, len); + if(wdaynum != -1) + found = TRUE; + } + if(!found && (monnum == -1)) { + monnum = checkmonth(date, len); + if(monnum != -1) + found = TRUE; + } + if(!found && (tzoff == -1)) { + /* this just must be a time zone string */ + tzoff = checktz(date, len); + if(tzoff != -1) + found = TRUE; + } + } if(!found) return PARSEDATE_FAIL; /* bad string */ @@ -362,18 +411,10 @@ static int parsedate(const char *date, time_t *output) /* a digit */ int val; char *end; - int len = 0; if((secnum == -1) && - (3 == sscanf(date, "%02d:%02d:%02d%n", - &hournum, &minnum, &secnum, &len))) { - /* time stamp! */ - date += len; - } - else if((secnum == -1) && - (2 == sscanf(date, "%02d:%02d%n", &hournum, &minnum, &len))) { - /* time stamp without seconds */ - date += len; - secnum = 0; + match_time(date, &hournum, &minnum, &secnum, &end)) { + /* time stamp */ + date = end; } else { long lval; diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index a222888..6092b78 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c @@ -87,8 +87,6 @@ static char *max5data(curl_off_t bytes, char *max5) CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE, (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) ); -#if (SIZEOF_CURL_OFF_T > 4) - else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE) /* 'XXXXM' is good until we're at 10000MB or above */ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); @@ -111,15 +109,8 @@ static char *max5data(curl_off_t bytes, char *max5) /* up to 10000PB, display without decimal: XXXXP */ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE); - /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number - can hold, but our data type is signed so 8192PB will be the maximum. */ - -#else - - else - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); - -#endif + /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number can + hold, but our data type is signed so 8192PB will be the maximum. */ return max5; } diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index 4b6ac07..9abb722 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -30,6 +30,10 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif +#ifdef HAVE_ARC4RANDOM +/* Some platforms might have the prototype missing (ubuntu + libressl) */ +uint32_t arc4random(void); +#endif #include <curl/curl.h> #include "vtls/vtls.h" @@ -143,6 +147,11 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) } #endif +#ifdef HAVE_ARC4RANDOM + *rnd = (unsigned int)arc4random(); + return CURLE_OK; +#endif + #if defined(RANDOM_FILE) && !defined(WIN32) if(!seeded) { /* if there's a random file to read a seed from, use it */ diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index 9d27929..aef3560 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c @@ -145,7 +145,8 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data, (void)data; if(checks_to_perform & CONNCHECK_ISDEAD) { - if(!Curl_conn_is_alive(data, conn)) + bool input_pending; + if(!Curl_conn_is_alive(data, conn, &input_pending)) ret_val |= CONNRESULT_DEAD; } @@ -755,12 +756,14 @@ CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len) CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header) { - long CSeq = 0; - if(checkprefix("CSeq:", header)) { - /* Store the received CSeq. Match is verified in rtsp_done */ - int nc = sscanf(&header[4], ": %ld", &CSeq); - if(nc == 1) { + long CSeq = 0; + char *endp; + char *p = &header[5]; + while(ISBLANK(*p)) + p++; + CSeq = strtol(p, &endp, 10); + if(p != endp) { struct RTSP *rtsp = data->req.p.rtsp; rtsp->CSeq_recv = CSeq; /* mark the request */ data->state.rtsp_CSeq_recv = CSeq; /* update the handle */ diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index 3b8d468..61cce61 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -230,14 +230,14 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ if(readfd0 != CURL_SOCKET_BAD) { if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) r |= CURL_CSELECT_IN; - if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL)) + if(pfd[num].revents & (POLLPRI|POLLNVAL)) r |= CURL_CSELECT_ERR; num++; } if(readfd1 != CURL_SOCKET_BAD) { if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) r |= CURL_CSELECT_IN2; - if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL)) + if(pfd[num].revents & (POLLPRI|POLLNVAL)) r |= CURL_CSELECT_ERR; num++; } diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index 604693a..6bb8879 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -899,7 +899,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURL_HTTP_VERSION_NONE: #ifdef USE_HTTP2 /* TODO: this seems an undesirable quirk to force a behaviour on - * lower implementations that they should recognize independantly? */ + * lower implementations that they should recognize independently? */ arg = CURL_HTTP_VERSION_2TLS; #endif /* accepted */ @@ -2369,7 +2369,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.use_ssl = (curl_usessl)arg; + data->set.use_ssl = (unsigned char)arg; break; case CURLOPT_SSL_OPTIONS: @@ -2849,7 +2849,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.fnmatch = va_arg(param, curl_fnmatch_callback); break; case CURLOPT_CHUNK_DATA: - data->wildcard.customptr = va_arg(param, void *); + data->set.wildcardptr = va_arg(param, void *); break; case CURLOPT_FNMATCH_DATA: data->set.fnmatch_data = va_arg(param, void *); diff --git a/Utilities/cmcurl/lib/sigpipe.h b/Utilities/cmcurl/lib/sigpipe.h index 14ab25b..48761ad 100644 --- a/Utilities/cmcurl/lib/sigpipe.h +++ b/Utilities/cmcurl/lib/sigpipe.h @@ -50,7 +50,6 @@ static void sigpipe_ignore(struct Curl_easy *data, if(!data->set.no_signal) { struct sigaction action; /* first, extract the existing situation */ - memset(&ig->old_pipe_act, 0, sizeof(struct sigaction)); sigaction(SIGPIPE, NULL, &ig->old_pipe_act); action = ig->old_pipe_act; /* ignore this signal */ diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index dc0abe7..0762004 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -25,8 +25,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) #define BUILDING_CURL_SMB_C diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index a964bfd..e4ffd85 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c @@ -770,22 +770,32 @@ static void printsub(struct Curl_easy *data, } } +static bool str_is_nonascii(const char *str) +{ + size_t len = strlen(str); + while(len--) { + if(*str & 0x80) + return TRUE; + str++; + } + return FALSE; +} + static CURLcode check_telnet_options(struct Curl_easy *data) { struct curl_slist *head; struct curl_slist *beg; - char option_keyword[128] = ""; - char option_arg[256] = ""; struct TELNET *tn = data->req.p.telnet; - struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - int binary_option; /* Add the user name as an environment variable if it was given on the command line */ if(data->state.aptr.user) { - msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user); - beg = curl_slist_append(tn->telnet_vars, option_arg); + char buffer[256]; + if(str_is_nonascii(data->conn->user)) + return CURLE_BAD_FUNCTION_ARGUMENT; + msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); + beg = curl_slist_append(tn->telnet_vars, buffer); if(!beg) { curl_slist_free_all(tn->telnet_vars); tn->telnet_vars = NULL; @@ -795,68 +805,100 @@ static CURLcode check_telnet_options(struct Curl_easy *data) tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; } - for(head = data->set.telnet_options; head; head = head->next) { - if(sscanf(head->data, "%127[^= ]%*[ =]%255s", - option_keyword, option_arg) == 2) { - - /* Terminal type */ - if(strcasecompare(option_keyword, "TTYPE")) { - strncpy(tn->subopt_ttype, option_arg, 31); - tn->subopt_ttype[31] = 0; /* String termination */ - tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; + for(head = data->set.telnet_options; head && !result; head = head->next) { + size_t olen; + char *option = head->data; + char *arg; + char *sep = strchr(option, '='); + if(sep) { + olen = sep - option; + arg = ++sep; + if(str_is_nonascii(arg)) continue; - } + switch(olen) { + case 5: + /* Terminal type */ + if(strncasecompare(option, "TTYPE", 5)) { + strncpy(tn->subopt_ttype, arg, 31); + tn->subopt_ttype[31] = 0; /* String termination */ + tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; + } + else + result = CURLE_UNKNOWN_OPTION; + break; - /* Display variable */ - if(strcasecompare(option_keyword, "XDISPLOC")) { - strncpy(tn->subopt_xdisploc, option_arg, 127); - tn->subopt_xdisploc[127] = 0; /* String termination */ - tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; - continue; - } + case 8: + /* Display variable */ + if(strncasecompare(option, "XDISPLOC", 8)) { + strncpy(tn->subopt_xdisploc, arg, 127); + tn->subopt_xdisploc[127] = 0; /* String termination */ + tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; + } + else + result = CURLE_UNKNOWN_OPTION; + break; - /* Environment variable */ - if(strcasecompare(option_keyword, "NEW_ENV")) { - beg = curl_slist_append(tn->telnet_vars, option_arg); - if(!beg) { - result = CURLE_OUT_OF_MEMORY; - break; + case 7: + /* Environment variable */ + if(strncasecompare(option, "NEW_ENV", 7)) { + beg = curl_slist_append(tn->telnet_vars, arg); + if(!beg) { + result = CURLE_OUT_OF_MEMORY; + break; + } + tn->telnet_vars = beg; + tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; } - tn->telnet_vars = beg; - tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; - continue; - } + else + result = CURLE_UNKNOWN_OPTION; + break; - /* Window Size */ - if(strcasecompare(option_keyword, "WS")) { - if(sscanf(option_arg, "%hu%*[xX]%hu", - &tn->subopt_wsx, &tn->subopt_wsy) == 2) - tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; - else { - failf(data, "Syntax error in telnet option: %s", head->data); - result = CURLE_SETOPT_OPTION_SYNTAX; - break; + case 2: + /* Window Size */ + if(strncasecompare(option, "WS", 2)) { + char *p; + unsigned long x = strtoul(arg, &p, 10); + unsigned long y = 0; + if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') { + p++; + y = strtoul(p, NULL, 10); + if(y && (y <= 0xffff)) { + tn->subopt_wsx = (unsigned short)x; + tn->subopt_wsy = (unsigned short)y; + tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; + } + } + if(!y) { + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_SETOPT_OPTION_SYNTAX; + } } - continue; - } + else + result = CURLE_UNKNOWN_OPTION; + break; - /* To take care or not of the 8th bit in data exchange */ - if(strcasecompare(option_keyword, "BINARY")) { - binary_option = atoi(option_arg); - if(binary_option != 1) { - tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; - tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; + case 6: + /* To take care or not of the 8th bit in data exchange */ + if(strncasecompare(option, "BINARY", 6)) { + int binary_option = atoi(arg); + if(binary_option != 1) { + tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; + tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; + } } - continue; + else + result = CURLE_UNKNOWN_OPTION; + break; + default: + failf(data, "Unknown telnet option %s", head->data); + result = CURLE_UNKNOWN_OPTION; + break; } - - failf(data, "Unknown telnet option %s", head->data); - result = CURLE_UNKNOWN_OPTION; - break; } - failf(data, "Syntax error in telnet option: %s", head->data); - result = CURLE_SETOPT_OPTION_SYNTAX; - break; + else { + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_SETOPT_OPTION_SYNTAX; + } } if(result) { @@ -881,8 +923,6 @@ static void suboption(struct Curl_easy *data) ssize_t bytes_written; size_t len; int err; - char varname[128] = ""; - char varval[128] = ""; struct TELNET *tn = data->req.p.telnet; struct connectdata *conn = data->conn; @@ -920,19 +960,18 @@ static void suboption(struct Curl_easy *data) for(v = tn->telnet_vars; v; v = v->next) { size_t tmplen = (strlen(v->data) + 1); - /* Add the variable only if it fits */ + /* Add the variable if it fits */ if(len + tmplen < (int)sizeof(temp)-6) { - int rv; - char sep[2] = ""; - varval[0] = 0; - rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval); - if(rv == 1) + char *s = strchr(v->data, ','); + if(!s) len += msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%s", CURL_NEW_ENV_VAR, varname); - else if(rv >= 2) + "%c%s", CURL_NEW_ENV_VAR, v->data); + else { + size_t vlen = s - v->data; len += msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%s%c%s", CURL_NEW_ENV_VAR, varname, - CURL_NEW_ENV_VALUE, varval); + "%c%.*s%c%s", CURL_NEW_ENV_VAR, + (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s); + } } } msnprintf((char *)&temp[len], sizeof(temp) - len, diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 69df214..a283952 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -980,7 +980,15 @@ static CURLcode readwrite_upload(struct Curl_easy *data, if(result) return result; - win_update_buffer_size(conn->writesockfd); +#if defined(WIN32) && defined(USE_WINSOCK) + { + struct curltime n = Curl_now(); + if(Curl_timediff(n, k->last_sndbuf_update) > 1000) { + win_update_buffer_size(conn->writesockfd); + k->last_sndbuf_update = n; + } + } +#endif if(k->pendingheader) { /* parts of what was sent was header */ @@ -1226,8 +1234,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, } /* Now update the "done" boolean we return */ - *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND| - KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE; + *done = (0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) ? TRUE : FALSE; result = CURLE_OK; out: if(result) @@ -1394,7 +1401,13 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) #ifndef CURL_DISABLE_FTP data->state.wildcardmatch = data->set.wildcard_enabled; if(data->state.wildcardmatch) { - struct WildcardData *wc = &data->wildcard; + struct WildcardData *wc; + if(!data->wildcard) { + data->wildcard = calloc(1, sizeof(struct WildcardData)); + if(!data->wildcard) + return CURLE_OUT_OF_MEMORY; + } + wc = data->wildcard; if(wc->state < CURLWC_INIT) { result = Curl_wildcard_init(wc); /* init wildcard structures */ if(result) diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index 1bb93df..f7b4bbb 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -288,33 +288,6 @@ static const struct Curl_handler * const protocols[] = { (struct Curl_handler *) NULL }; -/* - * Dummy handler for undefined protocol schemes. - */ - -static const struct Curl_handler Curl_handler_dummy = { - "<no protocol>", /* scheme */ - ZERO_NULL, /* setup_connection */ - ZERO_NULL, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - 0, /* defport */ - 0, /* protocol */ - 0, /* family */ - PROTOPT_NONE /* flags */ -}; - void Curl_freeset(struct Curl_easy *data) { /* Free all dynamic strings stored in the data->set substructure. */ @@ -341,6 +314,11 @@ void Curl_freeset(struct Curl_easy *data) data->state.url = NULL; Curl_mime_cleanpart(&data->set.mimepost); + +#ifndef CURL_DISABLE_COOKIES + curl_slist_free_all(data->set.cookielist); + data->set.cookielist = NULL; +#endif } /* free the URL pieces */ @@ -431,9 +409,6 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_dyn_free(&data->state.headerb); Curl_safefree(data->state.ulbuf); Curl_flush_cookies(data, TRUE); -#ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(data->set.cookielist); /* clean up list */ -#endif Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); Curl_altsvc_cleanup(&data->asi); Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); @@ -752,8 +727,6 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); - - Curl_llist_destroy(&conn->easyq, NULL); Curl_safefree(conn->localdev); Curl_free_primary_ssl_config(&conn->ssl_config); @@ -823,7 +796,7 @@ void Curl_disconnect(struct Curl_easy *data, disconnect and shutdown */ Curl_attach_connection(data, conn); - if(conn->handler->disconnect) + if(conn->handler && conn->handler->disconnect) /* This is set if protocol-specific cleanups should be made */ conn->handler->disconnect(data, conn, dead_connection); @@ -965,7 +938,20 @@ static bool extract_if_dead(struct connectdata *conn, } else { - dead = !Curl_conn_is_alive(data, conn); + bool input_pending; + + dead = !Curl_conn_is_alive(data, conn, &input_pending); + if(input_pending) { + /* For reuse, we want a "clean" connection state. The includes + * that we expect - in general - no waiting input data. Input + * waiting might be a TLS Notify Close, for example. We reject + * that. + * For protocols where data from other other end may arrive at + * any time (HTTP/2 PING for example), the protocol handler needs + * to install its own `connection_check` callback. + */ + dead = TRUE; + } } if(dead) { @@ -1170,14 +1156,14 @@ ConnectionExists(struct Curl_easy *data, continue; } } + } - if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { - foundPendingCandidate = TRUE; - /* Don't pick a connection that hasn't connected yet */ - infof(data, "Connection #%ld isn't open enough, can't reuse", - check->connection_id); - continue; - } + if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { + foundPendingCandidate = TRUE; + /* Don't pick a connection that hasn't connected yet */ + infof(data, "Connection #%ld isn't open enough, can't reuse", + check->connection_id); + continue; } #ifdef USE_UNIX_SOCKETS @@ -1291,6 +1277,11 @@ ConnectionExists(struct Curl_easy *data, } } + /* GSS delegation differences do not actually affect every connection + and auth method, but this check takes precaution before efficiency */ + if(needle->gssapi_delegation != check->gssapi_delegation) + continue; + /* If multiplexing isn't enabled on the h2 connection and h1 is explicitly requested, handle it: */ if((needle->handler->protocol & PROTO_FAMILY_HTTP) && @@ -1299,11 +1290,24 @@ ConnectionExists(struct Curl_easy *data, || ((check->httpversion >= 30) && (data->state.httpwant < CURL_HTTP_VERSION_3)))) continue; - - if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) { +#ifdef USE_SSH + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { if(!ssh_config_matches(needle, check)) continue; } +#endif +#ifndef CURL_DISABLE_FTP + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { + /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ + if(Curl_timestrcmp(needle->proto.ftpc.account, + check->proto.ftpc.account) || + Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, + check->proto.ftpc.alternative_to_user) || + (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) || + (needle->proto.ftpc.ccc != check->proto.ftpc.ccc)) + continue; + } +#endif if((needle->handler->flags&PROTOPT_SSL) #ifndef CURL_DISABLE_PROXY @@ -1494,10 +1498,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) if(!conn) return NULL; - conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined - already from start to avoid NULL - situations and checks */ - /* and we setup a few fields in case we end up actually using this struct */ conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ @@ -1589,11 +1589,11 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; conn->lastused = Curl_now(); /* used now */ + conn->gssapi_delegation = data->set.gssapi_delegation; return conn; error: - Curl_llist_destroy(&conn->easyq, NULL); free(conn->localdev); free(conn); return NULL; diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index 29927b3..62e3233 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c @@ -57,6 +57,15 @@ /* scheme is not URL encoded, the longest libcurl supported ones are... */ #define MAX_SCHEME_LEN 40 +/* + * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * sure we have _some_ value for AF_INET6 without polluting our fake value + * everywhere. + */ +#if !defined(ENABLE_IPV6) && !defined(AF_INET6) +#define AF_INET6 (AF_INET + 1) +#endif + /* Internal representation of CURLU. Point to URL-encoded strings. */ struct Curl_URL { char *scheme; @@ -599,7 +608,8 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, return CURLUE_BAD_IPV6; /* hostname is fine */ } -#ifdef ENABLE_IPV6 + + /* Check the IPv6 address. */ { char dest[16]; /* fits a binary IPv6 address */ char norm[MAX_IPADR_LEN]; @@ -616,11 +626,10 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, } hostname[hlen] = ']'; /* restore ending bracket */ } -#endif } else { /* letters from the second string are not ok */ - len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()"); + len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%"); if(hlen != len) /* hostname with bad content */ return CURLUE_BAD_HOSTNAME; @@ -1341,7 +1350,7 @@ void curl_url_cleanup(CURLU *u) } \ } while(0) -CURLU *curl_url_dup(CURLU *in) +CURLU *curl_url_dup(const CURLU *in) { struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1); if(u) { @@ -1362,10 +1371,10 @@ CURLU *curl_url_dup(CURLU *in) return NULL; } -CURLUcode curl_url_get(CURLU *u, CURLUPart what, +CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char **part, unsigned int flags) { - char *ptr; + const char *ptr; CURLUcode ifmissing = CURLUE_UNKNOWN_PART; char portbuf[7]; bool urldecode = (flags & CURLU_URLDECODE)?1:0; @@ -1432,11 +1441,8 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, break; case CURLUPART_PATH: ptr = u->path; - if(!ptr) { - ptr = u->path = strdup("/"); - if(!u->path) - return CURLUE_OUT_OF_MEMORY; - } + if(!ptr) + ptr = "/"; break; case CURLUPART_QUERY: ptr = u->query; @@ -1546,8 +1552,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, return CURLUE_OUT_OF_MEMORY; host++; } - free(u->host); - u->host = Curl_dyn_ptr(&enc); + allochost = Curl_dyn_ptr(&enc); } } diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index 4cfffa7..8b54518 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -168,7 +168,7 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, #include "rtsp.h" #include "smb.h" #include "mqtt.h" -#include "wildcard.h" +#include "ftplistparser.h" #include "multihandle.h" #include "c-hyper.h" #include "cf-socket.h" @@ -687,6 +687,10 @@ struct SingleRequest { #ifndef CURL_DISABLE_DOH struct dohdata *doh; /* DoH specific data for this request */ #endif +#if defined(WIN32) && defined(USE_WINSOCK) + struct curltime last_sndbuf_update; /* last time readwrite_upload called + win_update_buffer_size */ +#endif unsigned char setcookies; unsigned char writer_stack_depth; /* Unencoding stack depth. */ BIT(header); /* incoming data has HTTP header */ @@ -1057,6 +1061,7 @@ struct connectdata { unsigned char ip_version; /* copied from the Curl_easy at creation time */ unsigned char httpversion; /* the HTTP version*10 reported by the server */ unsigned char connect_only; + unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ }; /* The end of connectdata. */ @@ -1374,7 +1379,7 @@ struct UrlState { struct dynbuf trailers_buf; /* a buffer containing the compiled trailing headers */ struct Curl_llist httphdrs; /* received headers */ - struct curl_header headerout; /* for external purposes */ + struct curl_header headerout[2]; /* for external purposes */ struct Curl_header_store *prevhead; /* the latest added header */ trailers_state trailers_state; /* whether we are sending trailers and what stage are we at */ @@ -1713,8 +1718,6 @@ struct UserDefined { #ifndef CURL_DISABLE_NETRC unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ #endif - curl_usessl use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! */ unsigned int new_file_perms; /* when creating remote files */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ struct curl_blob *blobs[BLOB_LAST]; @@ -1739,6 +1742,7 @@ struct UserDefined { curl_fnmatch_callback fnmatch; /* callback to decide which file corresponds to pattern (e.g. if WILDCARDMATCH is on) */ void *fnmatch_data; + void *wildcardptr; #endif /* GSS-API credential delegation, see the documentation of CURLOPT_GSSAPI_DELEGATION */ @@ -1773,6 +1777,8 @@ struct UserDefined { BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some recipients */ #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 */ BIT(is_fread_set); /* has read callback been set to non-NULL? */ @@ -1934,7 +1940,7 @@ struct Curl_easy { struct UrlState state; /* struct for fields used for state info and other dynamic purposes */ #ifndef CURL_DISABLE_FTP - struct WildcardData wildcard; /* wildcard download state info */ + struct WildcardData *wildcard; /* wildcard download state info */ #endif struct PureInfo info; /* stats, reports and info data */ struct curl_tlssessioninfo tsi; /* Information about the TLS session, only diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index 55b54a0..5800ad3 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c @@ -62,7 +62,15 @@ #endif #ifdef HAVE_BROTLI +#if defined(__GNUC__) +/* Ignore -Wvla warnings in brotli headers */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wvla" +#endif #include <brotli/decode.h> +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif #endif #ifdef HAVE_ZSTD @@ -357,8 +365,7 @@ static const char * const protocols[] = { #ifdef USE_SSH "sftp", #endif -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) "smb", # ifdef USE_SSL "smbs", diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c index 1930703..5308999 100644 --- a/Utilities/cmcurl/lib/vquic/curl_msh3.c +++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c @@ -548,7 +548,6 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, struct Curl_easy *data, int event, int arg1, void *arg2) { - struct cf_msh3_ctx *ctx = cf->ctx; struct HTTP *stream = data->req.p.http; CURLcode result = CURLE_OK; @@ -579,11 +578,6 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, DEBUGF(LOG_CF(data, cf, "req: update info")); cf_msh3_active(cf, data); break; - case CF_CTRL_CONN_REPORT_STATS: - if(cf->sockindex == FIRSTSOCKET) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); - break; - default: break; } @@ -753,6 +747,19 @@ static CURLcode cf_msh3_query(struct Curl_cfilter *cf, *pres1 = 100; return CURLE_OK; } + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + /* we do not know when the first byte arrived */ + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } default: break; } @@ -762,11 +769,13 @@ static CURLcode cf_msh3_query(struct Curl_cfilter *cf, } static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + bool *input_pending) { struct cf_msh3_ctx *ctx = cf->ctx; (void)data; + *input_pending = FALSE; return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn && ctx->connected; } diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c index ffdaead..d2d0a3a 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c @@ -64,6 +64,8 @@ #include "vtls/vtls.h" #include "curl_ngtcp2.h" +#include "warnless.h" + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -901,7 +903,7 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, rv |= GETSOCK_READSOCK(0); /* we're still uploading or the HTTP/2 layer wants to send data */ - if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND && + if((k->keepon & KEEP_SENDBITS) == KEEP_SEND && (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) && ngtcp2_conn_get_cwnd_left(ctx->qconn) && ngtcp2_conn_get_max_data_left(ctx->qconn) && @@ -951,7 +953,7 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, } /* - * write_resp_raw() copies resonse data in raw format to the `data`'s + * write_resp_raw() copies response data in raw format to the `data`'s * receive buffer. If not enough space is available, it appends to the * `data`'s overflow buffer. */ @@ -1762,7 +1764,7 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf, ssize_t recvd; int rv; uint8_t buf[65536]; - size_t bufsize = sizeof(buf); + int bufsize = (int)sizeof(buf); size_t pktcount = 0, total_recvd = 0; struct sockaddr_storage remote_addr; socklen_t remote_addrlen; @@ -2107,13 +2109,6 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, } } break; - case CF_CTRL_CONN_REPORT_STATS: - if(cf->sockindex == FIRSTSOCKET) { - if(ctx->got_first_byte) - Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at); - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); - } - break; default: break; } @@ -2127,7 +2122,6 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) if(ctx->qlogfd != -1) { close(ctx->qlogfd); - ctx->qlogfd = -1; } #ifdef USE_OPENSSL if(ctx->ssl) @@ -2155,6 +2149,7 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) ngtcp2_conn_del(ctx->qconn); memset(ctx, 0, sizeof(*ctx)); + ctx->qlogfd = -1; ctx->call_data = save; } @@ -2176,7 +2171,7 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) (uint8_t *)buffer, sizeof(buffer), &ctx->last_error, ts); if(rc > 0) { - while((send(ctx->q.sockfd, buffer, rc, 0) == -1) && + while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && SOCKERRNO == EINTR); } @@ -2200,6 +2195,7 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) } cf->ctx = NULL; /* No CF_DATA_RESTORE(cf, save) possible */ + (void)save; } /* @@ -2428,6 +2424,18 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, else *pres1 = -1; return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + if(ctx->got_first_byte) + *when = ctx->first_byte_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } default: break; } @@ -2436,6 +2444,32 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, CURLE_UNKNOWN_OPTION; } +static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + bool alive = TRUE; + + *input_pending = FALSE; + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + return FALSE; + + if(*input_pending) { + /* This happens before we've sent off a request and the connection is + not in use by any other transfer, there shouldn't be any data here, + only "protocol frames" */ + *input_pending = FALSE; + Curl_attach_connection(data, cf->conn); + if(cf_process_ingress(cf, data)) + alive = FALSE; + else { + alive = TRUE; + } + Curl_detach_connection(data); + } + + return alive; +} struct Curl_cftype Curl_cft_http3 = { "HTTP/3", @@ -2450,7 +2484,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_ngtcp2_send, cf_ngtcp2_recv, cf_ngtcp2_data_event, - Curl_cf_def_conn_is_alive, + cf_ngtcp2_conn_is_alive, Curl_cf_def_conn_keep_alive, cf_ngtcp2_query, }; @@ -2470,6 +2504,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } + ctx->qlogfd = -1; cf_ngtcp2_ctx_clear(ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c index 54408d7..87a221c 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.c +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c @@ -444,7 +444,7 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1; uint8_t buf[65536]; - size_t bufsize = sizeof(buf); + int bufsize = (int)sizeof(buf); struct sockaddr_storage remote_addr; socklen_t remote_addrlen; quiche_recv_info recv_info; @@ -950,7 +950,7 @@ static int cf_quiche_get_select_socks(struct Curl_cfilter *cf, rv |= GETSOCK_READSOCK(0); /* we're still uploading or the HTTP/3 layer wants to send data */ - if(((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) + if(((k->keepon & KEEP_SENDBITS) == KEEP_SEND) && stream_is_writeable(cf, data)) rv |= GETSOCK_WRITESOCK(0); @@ -1016,13 +1016,6 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, case CF_CTRL_DATA_IDLE: /* anything to do? */ break; - case CF_CTRL_CONN_REPORT_STATS: - if(cf->sockindex == FIRSTSOCKET) { - if(ctx->got_first_byte) - Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at); - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); - } - break; default: break; } @@ -1346,6 +1339,18 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, else *pres1 = -1; return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + if(ctx->got_first_byte) + *when = ctx->first_byte_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } default: break; } @@ -1354,6 +1359,32 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, CURLE_UNKNOWN_OPTION; } +static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + bool alive = TRUE; + + *input_pending = FALSE; + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + return FALSE; + + if(*input_pending) { + /* This happens before we've sent off a request and the connection is + not in use by any other transfer, there shouldn't be any data here, + only "protocol frames" */ + *input_pending = FALSE; + Curl_attach_connection(data, cf->conn); + if(cf_process_ingress(cf, data)) + alive = FALSE; + else { + alive = TRUE; + } + Curl_detach_connection(data); + } + + return alive; +} struct Curl_cftype Curl_cft_http3 = { "HTTP/3", @@ -1368,7 +1399,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_quiche_send, cf_quiche_recv, cf_quiche_data_event, - Curl_cf_def_conn_is_alive, + cf_quiche_conn_is_alive, Curl_cf_def_conn_keep_alive, cf_quiche_query, }; diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index 5f4f30d..bbdeabd 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c @@ -167,7 +167,8 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, *psent = 0; - while((sent = send(qctx->sockfd, (const char *)pkt, pktlen, 0)) == -1 && + while((sent = send(qctx->sockfd, + (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 && SOCKERRNO == EINTR) ; @@ -363,6 +364,10 @@ bool Curl_conn_is_http3(const struct Curl_easy *data, CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn) { + if(conn->transport == TRNSPRT_UNIX) { + /* cannot do QUIC over a unix domain socket */ + return CURLE_QUIC_CONNECT_ERROR; + } if(!(conn->handler->flags & PROTOPT_SSL)) { failf(data, "HTTP/3 requested for non-HTTPS URL"); return CURLE_URL_MALFORMAT; diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c index 1115318..b31f741 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c @@ -685,7 +685,6 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) struct ssh_conn *sshc = &conn->proto.sshc; curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc = SSH_NO_ERROR, err; - char *new_readdir_line; int seekerr = CURL_SEEKFUNC_OK; const char *err_msg; *block = 0; /* we're not blocking by default */ @@ -1432,7 +1431,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SFTP_READDIR: - + Curl_dyn_reset(&sshc->readdir_buf); if(sshc->readdir_attrs) sftp_attributes_free(sshc->readdir_attrs); @@ -1468,17 +1467,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) sshc->readdir_len); } else { - sshc->readdir_currLen = strlen(sshc->readdir_longentry); - sshc->readdir_totalLen = 80 + sshc->readdir_currLen; - sshc->readdir_line = calloc(sshc->readdir_totalLen, 1); - if(!sshc->readdir_line) { - state(data, SSH_SFTP_CLOSE); + if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { sshc->actualcode = CURLE_OUT_OF_MEMORY; + state(data, SSH_STOP); break; } - memcpy(sshc->readdir_line, sshc->readdir_longentry, - sshc->readdir_currLen); if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == SSH_S_IFLNK)) { @@ -1541,24 +1535,11 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) Curl_safefree(sshc->readdir_linkPath); - /* get room for the filename and extra output */ - sshc->readdir_totalLen += 4 + sshc->readdir_len; - new_readdir_line = Curl_saferealloc(sshc->readdir_line, - sshc->readdir_totalLen); - if(!new_readdir_line) { - sshc->readdir_line = NULL; - state(data, SSH_SFTP_CLOSE); + if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s", + sshc->readdir_filename)) { sshc->actualcode = CURLE_OUT_OF_MEMORY; break; } - sshc->readdir_line = new_readdir_line; - - sshc->readdir_currLen += msnprintf(sshc->readdir_line + - sshc->readdir_currLen, - sshc->readdir_totalLen - - sshc->readdir_currLen, - " -> %s", - sshc->readdir_filename); sftp_attributes_free(sshc->readdir_link_attrs); sshc->readdir_link_attrs = NULL; @@ -1568,21 +1549,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_SFTP_READDIR_BOTTOM); /* FALLTHROUGH */ case SSH_SFTP_READDIR_BOTTOM: - sshc->readdir_currLen += msnprintf(sshc->readdir_line + - sshc->readdir_currLen, - sshc->readdir_totalLen - - sshc->readdir_currLen, "\n"); - result = Curl_client_write(data, CLIENTWRITE_BODY, - sshc->readdir_line, - sshc->readdir_currLen); + if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1)) + result = CURLE_OUT_OF_MEMORY; + else + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&sshc->readdir_buf), + Curl_dyn_len(&sshc->readdir_buf)); if(!result) { /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line, - sshc->readdir_currLen); - data->req.bytecount += sshc->readdir_currLen; + Curl_debug(data, CURLINFO_DATA_OUT, Curl_dyn_ptr(&sshc->readdir_buf), + Curl_dyn_len(&sshc->readdir_buf)); + data->req.bytecount += Curl_dyn_len(&sshc->readdir_buf); } - Curl_safefree(sshc->readdir_line); ssh_string_free_char(sshc->readdir_tmp); sshc->readdir_tmp = NULL; @@ -2021,7 +2000,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) Curl_safefree(sshc->rsa); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); - Curl_safefree(sshc->readdir_line); + Curl_dyn_free(&sshc->readdir_buf); Curl_safefree(sshc->readdir_linkPath); SSH_STRING_FREE_CHAR(sshc->homedir); @@ -2166,11 +2145,12 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, struct connectdata *conn) { struct SSHPROTO *ssh; - (void)conn; + struct ssh_conn *sshc = &conn->proto.sshc; data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); if(!ssh) return CURLE_OUT_OF_MEMORY; + Curl_dyn_init(&sshc->readdir_buf, PATH_MAX * 2); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index 4703eb5..f1154dc 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c @@ -100,10 +100,11 @@ /* Local functions: */ static const char *sftp_libssh2_strerror(unsigned long err); +#ifdef CURL_LIBSSH2_DEBUG static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); static LIBSSH2_FREE_FUNC(my_libssh2_free); - +#endif static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data); static CURLcode ssh_connect(struct Curl_easy *data, bool *done); static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done); @@ -283,6 +284,8 @@ static CURLcode libssh2_session_error_to_CURLE(int err) return CURLE_SSH; } +#ifdef CURL_LIBSSH2_DEBUG + static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc) { (void)abstract; /* arg not used */ @@ -302,6 +305,8 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free) free(ptr); } +#endif + /* * SSH State machine related code */ @@ -2400,7 +2405,6 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename); if(result) { - sshc->readdir_line = NULL; Curl_safefree(sshp->readdir_filename); Curl_safefree(sshp->readdir_longentry); state(data, SSH_SFTP_CLOSE); @@ -3004,12 +3008,9 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) Curl_safefree(sshc->rsa_pub); Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); - Curl_safefree(sshc->homedir); - Curl_safefree(sshc->readdir_line); /* the code we are about to return */ result = sshc->actualcode; @@ -3268,9 +3269,13 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ +#ifdef CURL_LIBSSH2_DEBUG sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc, my_libssh2_free, my_libssh2_realloc, data); +#else + sshc->ssh_session = libssh2_session_init(); +#endif if(!sshc->ssh_session) { failf(data, "Failure initialising ssh session"); return CURLE_FAILED_INIT; diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h index 7c25555..1e1b137 100644 --- a/Utilities/cmcurl/lib/vssh/ssh.h +++ b/Utilities/cmcurl/lib/vssh/ssh.h @@ -147,7 +147,6 @@ struct ssh_conn { char *homedir; /* when doing SFTP we figure out home dir in the connect phase */ - char *readdir_line; /* end of READDIR stuff */ int secondCreateDirs; /* counter use by the code to see if the @@ -158,7 +157,8 @@ struct ssh_conn { #if defined(USE_LIBSSH) char *readdir_linkPath; - size_t readdir_len, readdir_totalLen, readdir_currLen; + size_t readdir_len; + struct dynbuf readdir_buf; /* our variables */ unsigned kbd_state; /* 0 or 1 */ ssh_key privkey; diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c index 774cbdd..12c0390 100644 --- a/Utilities/cmcurl/lib/vtls/nss.c +++ b/Utilities/cmcurl/lib/vtls/nss.c @@ -1536,36 +1536,6 @@ static void nss_cleanup(void) initialized = 0; } -/* - * This function uses SSL_peek to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -static int nss_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - int rc; - char buf; - - (void)data; - DEBUGASSERT(backend); - - rc = - PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK, - PR_SecondsToInterval(1)); - if(rc > 0) - return 1; /* connection still in place */ - - if(rc == 0) - return 0; /* connection has been closed */ - - return -1; /* connection status unknown */ -} - static void close_one(struct ssl_connect_data *connssl) { /* before the cleanup, check whether we are using a client certificate */ @@ -2524,7 +2494,7 @@ const struct Curl_ssl Curl_ssl_nss = { nss_init, /* init */ nss_cleanup, /* cleanup */ nss_version, /* version */ - nss_check_cxn, /* check_cxn */ + Curl_none_check_cxn, /* check_cxn */ /* NSS has no shutdown function provided and thus always fail */ Curl_none_shutdown, /* shutdown */ nss_data_pending, /* data_pending */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index e3a50bd..2ba53d9 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -1788,63 +1788,6 @@ static void ossl_cleanup(void) Curl_tls_keylog_close(); } -/* - * This function is used to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -static int ossl_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - /* SSL_peek takes data out of the raw recv buffer without peeking so we use - recv MSG_PEEK instead. Bug #795 */ -#ifdef MSG_PEEK - char buf; - ssize_t nread; - curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); - if(sock == CURL_SOCKET_BAD) - return 0; /* no socket, consider closed */ - nread = recv((RECV_TYPE_ARG1)sock, - (RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1, - (RECV_TYPE_ARG4)MSG_PEEK); - if(nread == 0) - return 0; /* connection has been closed */ - if(nread == 1) - return 1; /* connection still in place */ - else if(nread == -1) { - int err = SOCKERRNO; - if(err == EINPROGRESS || -#if defined(EAGAIN) && (EAGAIN != EWOULDBLOCK) - err == EAGAIN || -#endif - err == EWOULDBLOCK) - return 1; /* connection still in place */ - if(err == ECONNRESET || -#ifdef ECONNABORTED - err == ECONNABORTED || -#endif -#ifdef ENETDOWN - err == ENETDOWN || -#endif -#ifdef ENETRESET - err == ENETRESET || -#endif -#ifdef ESHUTDOWN - err == ESHUTDOWN || -#endif -#ifdef ETIMEDOUT - err == ETIMEDOUT || -#endif - err == ENOTCONN) - return 0; /* connection has been closed */ - } -#endif - (void)data; - return -1; /* connection status unknown */ -} - /* Selects an OpenSSL crypto engine */ static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine) @@ -4836,7 +4779,7 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_init, /* init */ ossl_cleanup, /* cleanup */ ossl_version, /* version */ - ossl_check_cxn, /* check_cxn */ + Curl_none_check_cxn, /* check_cxn */ ossl_shutdown, /* shutdown */ ossl_data_pending, /* data_pending */ ossl_random, /* random */ diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 452fa40..6f94c7e 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -264,128 +264,133 @@ set_ssl_version_min_max(DWORD *enabled_protocols, /* longest is 26, buffer is slightly bigger */ #define LONGEST_ALG_ID 32 -#define CIPHEROPTION(X) \ - if(strcmp(#X, tmp) == 0) \ - return X +#define CIPHEROPTION(x) {#x, x} -static int -get_alg_id_by_name(char *name) -{ - char tmp[LONGEST_ALG_ID] = { 0 }; - char *nameEnd = strchr(name, ':'); - size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name); +struct algo { + const char *name; + int id; +}; - /* reject too-long alg names */ - if(n > (LONGEST_ALG_ID - 1)) - return 0; - - strncpy(tmp, name, n); - tmp[n] = 0; - CIPHEROPTION(CALG_MD2); - CIPHEROPTION(CALG_MD4); - CIPHEROPTION(CALG_MD5); - CIPHEROPTION(CALG_SHA); - CIPHEROPTION(CALG_SHA1); - CIPHEROPTION(CALG_MAC); - CIPHEROPTION(CALG_RSA_SIGN); - CIPHEROPTION(CALG_DSS_SIGN); +static const struct algo algs[]= { + CIPHEROPTION(CALG_MD2), + CIPHEROPTION(CALG_MD4), + CIPHEROPTION(CALG_MD5), + CIPHEROPTION(CALG_SHA), + CIPHEROPTION(CALG_SHA1), + CIPHEROPTION(CALG_MAC), + CIPHEROPTION(CALG_RSA_SIGN), + CIPHEROPTION(CALG_DSS_SIGN), /* ifdefs for the options that are defined conditionally in wincrypt.h */ #ifdef CALG_NO_SIGN - CIPHEROPTION(CALG_NO_SIGN); + CIPHEROPTION(CALG_NO_SIGN), #endif - CIPHEROPTION(CALG_RSA_KEYX); - CIPHEROPTION(CALG_DES); + CIPHEROPTION(CALG_RSA_KEYX), + CIPHEROPTION(CALG_DES), #ifdef CALG_3DES_112 - CIPHEROPTION(CALG_3DES_112); + CIPHEROPTION(CALG_3DES_112), #endif - CIPHEROPTION(CALG_3DES); - CIPHEROPTION(CALG_DESX); - CIPHEROPTION(CALG_RC2); - CIPHEROPTION(CALG_RC4); - CIPHEROPTION(CALG_SEAL); + CIPHEROPTION(CALG_3DES), + CIPHEROPTION(CALG_DESX), + CIPHEROPTION(CALG_RC2), + CIPHEROPTION(CALG_RC4), + CIPHEROPTION(CALG_SEAL), #ifdef CALG_DH_SF - CIPHEROPTION(CALG_DH_SF); + CIPHEROPTION(CALG_DH_SF), #endif - CIPHEROPTION(CALG_DH_EPHEM); + CIPHEROPTION(CALG_DH_EPHEM), #ifdef CALG_AGREEDKEY_ANY - CIPHEROPTION(CALG_AGREEDKEY_ANY); + CIPHEROPTION(CALG_AGREEDKEY_ANY), #endif #ifdef CALG_HUGHES_MD5 - CIPHEROPTION(CALG_HUGHES_MD5); + CIPHEROPTION(CALG_HUGHES_MD5), #endif - CIPHEROPTION(CALG_SKIPJACK); + CIPHEROPTION(CALG_SKIPJACK), #ifdef CALG_TEK - CIPHEROPTION(CALG_TEK); + CIPHEROPTION(CALG_TEK), #endif - CIPHEROPTION(CALG_CYLINK_MEK); - CIPHEROPTION(CALG_SSL3_SHAMD5); + CIPHEROPTION(CALG_CYLINK_MEK), + CIPHEROPTION(CALG_SSL3_SHAMD5), #ifdef CALG_SSL3_MASTER - CIPHEROPTION(CALG_SSL3_MASTER); + CIPHEROPTION(CALG_SSL3_MASTER), #endif #ifdef CALG_SCHANNEL_MASTER_HASH - CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH); + CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH), #endif #ifdef CALG_SCHANNEL_MAC_KEY - CIPHEROPTION(CALG_SCHANNEL_MAC_KEY); + CIPHEROPTION(CALG_SCHANNEL_MAC_KEY), #endif #ifdef CALG_SCHANNEL_ENC_KEY - CIPHEROPTION(CALG_SCHANNEL_ENC_KEY); + CIPHEROPTION(CALG_SCHANNEL_ENC_KEY), #endif #ifdef CALG_PCT1_MASTER - CIPHEROPTION(CALG_PCT1_MASTER); + CIPHEROPTION(CALG_PCT1_MASTER), #endif #ifdef CALG_SSL2_MASTER - CIPHEROPTION(CALG_SSL2_MASTER); + CIPHEROPTION(CALG_SSL2_MASTER), #endif #ifdef CALG_TLS1_MASTER - CIPHEROPTION(CALG_TLS1_MASTER); + CIPHEROPTION(CALG_TLS1_MASTER), #endif #ifdef CALG_RC5 - CIPHEROPTION(CALG_RC5); + CIPHEROPTION(CALG_RC5), #endif #ifdef CALG_HMAC - CIPHEROPTION(CALG_HMAC); + CIPHEROPTION(CALG_HMAC), #endif #ifdef CALG_TLS1PRF - CIPHEROPTION(CALG_TLS1PRF); + CIPHEROPTION(CALG_TLS1PRF), #endif #ifdef CALG_HASH_REPLACE_OWF - CIPHEROPTION(CALG_HASH_REPLACE_OWF); + CIPHEROPTION(CALG_HASH_REPLACE_OWF), #endif #ifdef CALG_AES_128 - CIPHEROPTION(CALG_AES_128); + CIPHEROPTION(CALG_AES_128), #endif #ifdef CALG_AES_192 - CIPHEROPTION(CALG_AES_192); + CIPHEROPTION(CALG_AES_192), #endif #ifdef CALG_AES_256 - CIPHEROPTION(CALG_AES_256); + CIPHEROPTION(CALG_AES_256), #endif #ifdef CALG_AES - CIPHEROPTION(CALG_AES); + CIPHEROPTION(CALG_AES), #endif #ifdef CALG_SHA_256 - CIPHEROPTION(CALG_SHA_256); + CIPHEROPTION(CALG_SHA_256), #endif #ifdef CALG_SHA_384 - CIPHEROPTION(CALG_SHA_384); + CIPHEROPTION(CALG_SHA_384), #endif #ifdef CALG_SHA_512 - CIPHEROPTION(CALG_SHA_512); + CIPHEROPTION(CALG_SHA_512), #endif #ifdef CALG_ECDH - CIPHEROPTION(CALG_ECDH); + CIPHEROPTION(CALG_ECDH), #endif #ifdef CALG_ECMQV - CIPHEROPTION(CALG_ECMQV); + CIPHEROPTION(CALG_ECMQV), #endif #ifdef CALG_ECDSA - CIPHEROPTION(CALG_ECDSA); + CIPHEROPTION(CALG_ECDSA), #endif #ifdef CALG_ECDH_EPHEM - CIPHEROPTION(CALG_ECDH_EPHEM); + CIPHEROPTION(CALG_ECDH_EPHEM), #endif - return 0; + {NULL, 0}, +}; + +static int +get_alg_id_by_name(char *name) +{ + char *nameEnd = strchr(name, ':'); + size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name); + int i; + + for(i = 0; algs[i].name; i++) { + if((n == strlen(algs[i].name) && !strncmp(algs[i].name, name, n))) + return algs[i].id; + } + return 0; /* not found */ } #define NUM_CIPHERS 47 /* There are 47 options listed above */ @@ -1201,18 +1206,18 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* The first four bytes will be an unsigned int indicating number of bytes of data in the rest of the buffer. */ extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]); - cur += sizeof(unsigned int); + cur += (int)sizeof(unsigned int); /* The next four bytes are an indicator that this buffer will contain ALPN data, as opposed to NPN, for example. */ *(unsigned int *)(void *)&alpn_buffer[cur] = SecApplicationProtocolNegotiationExt_ALPN; - cur += sizeof(unsigned int); + cur += (int)sizeof(unsigned int); /* The next two bytes will be an unsigned short indicating the number of bytes used to list the preferred protocols. */ list_len = (unsigned short*)(void *)(&alpn_buffer[cur]); - cur += sizeof(unsigned short); + cur += (int)sizeof(unsigned short); list_start_index = cur; @@ -1225,7 +1230,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) cur += proto.len; *list_len = curlx_uitous(cur - list_start_index); - *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short); + *extension_len = *list_len + + (unsigned short)sizeof(unsigned int) + + (unsigned short)sizeof(unsigned short); InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur); InitSecBufferDesc(&inbuf_desc, &inbuf, 1); diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index 2e98169..7f55fb5 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -2150,50 +2150,39 @@ static long pem_to_der(const char *in, unsigned char **out, size_t *outlen) return sep_end - in; } +#define MAX_CERTS_SIZE (50*1024*1024) /* arbitrary - to catch mistakes */ + static int read_cert(const char *file, unsigned char **out, size_t *outlen) { int fd; - ssize_t n, len = 0, cap = 512; - unsigned char buf[512], *data; + ssize_t n; + unsigned char buf[512]; + struct dynbuf certs; + + Curl_dyn_init(&certs, MAX_CERTS_SIZE); fd = open(file, 0); if(fd < 0) return -1; - data = malloc(cap); - if(!data) { - close(fd); - return -1; - } - for(;;) { n = read(fd, buf, sizeof(buf)); + if(!n) + break; if(n < 0) { close(fd); - free(data); + Curl_dyn_free(&certs); return -1; } - else if(n == 0) { + if(Curl_dyn_addn(&certs, buf, n)) { close(fd); - break; - } - - if(len + n >= cap) { - cap *= 2; - data = Curl_saferealloc(data, cap); - if(!data) { - close(fd); - return -1; - } + return -1; } - - memcpy(data + len, buf, n); - len += n; } - data[len] = '\0'; + close(fd); - *out = data; - *outlen = len; + *out = Curl_dyn_uptr(&certs); + *outlen = Curl_dyn_len(&certs); return 0; } @@ -2202,16 +2191,18 @@ static int append_cert_to_array(struct Curl_easy *data, const unsigned char *buf, size_t buflen, CFMutableArrayRef array) { - CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen); char *certp; CURLcode result; + SecCertificateRef cacert; + CFDataRef certdata; + + certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen); if(!certdata) { failf(data, "SSL: failed to allocate array for CA certificate"); return CURLE_OUT_OF_MEMORY; } - SecCertificateRef cacert = - SecCertificateCreateWithData(kCFAllocatorDefault, certdata); + cacert = SecCertificateCreateWithData(kCFAllocatorDefault, certdata); CFRelease(certdata); if(!cacert) { failf(data, "SSL: failed to create SecCertificate from CA certificate"); @@ -2425,11 +2416,15 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, do { SecTrustRef trust; - OSStatus ret = SSLCopyPeerTrust(ctx, &trust); + OSStatus ret; + SecKeyRef keyRef; + OSStatus success; + + ret = SSLCopyPeerTrust(ctx, &trust); if(ret != noErr || !trust) break; - SecKeyRef keyRef = SecTrustCopyPublicKey(trust); + keyRef = SecTrustCopyPublicKey(trust); CFRelease(trust); if(!keyRef) break; @@ -2443,8 +2438,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, #elif SECTRANSP_PINNEDPUBKEY_V2 - OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, - &publicKeyBits); + success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, + &publicKeyBits); CFRelease(keyRef); if(success != errSecSuccess || !publicKeyBits) break; @@ -2987,12 +2982,13 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; + CURLcode result; DEBUGF(LOG_CF(data, cf, "connect_step3")); /* There is no step 3! * Well, okay, let's collect server certificates, and if verbose mode is on, * let's print the details of the server certificates. */ - const CURLcode result = collect_server_cert(cf, data); + result = collect_server_cert(cf, data); if(result) return result; @@ -3237,35 +3233,6 @@ static size_t sectransp_version(char *buffer, size_t size) return msnprintf(buffer, size, "SecureTransport"); } -/* - * This function uses SSLGetSessionState to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -static int sectransp_check_cxn(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - OSStatus err; - SSLSessionState state; - - (void)data; - DEBUGASSERT(backend); - - if(backend->ssl_ctx) { - DEBUGF(LOG_CF(data, cf, "check connection")); - err = SSLGetSessionState(backend->ssl_ctx, &state); - if(err == noErr) - return state == kSSLConnected || state == kSSLHandshake; - return -1; - } - return 0; -} - static bool sectransp_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -3410,13 +3377,15 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf, DEBUGASSERT(backend); again: + *curlcode = CURLE_OK; err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed); if(err != noErr) { switch(err) { case errSSLWouldBlock: /* return how much we read (if anything) */ - if(processed) + if(processed) { return (ssize_t)processed; + } *curlcode = CURLE_AGAIN; return -1L; break; @@ -3428,7 +3397,7 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf, case errSSLClosedGraceful: case errSSLClosedNoNotify: *curlcode = CURLE_OK; - return -1L; + return 0; break; /* The below is errSSLPeerAuthCompleted; it's not defined in @@ -3439,8 +3408,10 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf, CURLcode result = verify_cert(cf, data, conn_config->CAfile, conn_config->ca_info_blob, backend->ssl_ctx); - if(result) - return result; + if(result) { + *curlcode = result; + return -1; + } } goto again; default: @@ -3477,7 +3448,7 @@ const struct Curl_ssl Curl_ssl_sectransp = { Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ sectransp_version, /* version */ - sectransp_check_cxn, /* check_cxn */ + Curl_none_check_cxn, /* check_cxn */ sectransp_shutdown, /* shutdown */ sectransp_data_pending, /* data_pending */ sectransp_random, /* random */ diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index 15f6844..144e6ee 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -1604,16 +1604,11 @@ static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, struct Curl_easy *data, int event, int arg1, void *arg2) { - struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; (void)arg1; (void)arg2; switch(event) { - case CF_CTRL_CONN_REPORT_STATS: - if(cf->sockindex == FIRSTSOCKET && !Curl_ssl_cf_is_proxy(cf)) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); - break; case CF_CTRL_DATA_ATTACH: if(Curl_ssl->attach_data) { CF_DATA_SAVE(save, cf, data); @@ -1634,10 +1629,32 @@ static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, return CURLE_OK; } -static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode ssl_cf_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct ssl_connect_data *connssl = cf->ctx; + + switch(query) { + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected && !Curl_ssl_cf_is_proxy(cf)) + *when = connssl->handshake_done; + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, + bool *input_pending) { struct cf_call_data save; - bool result; + int result; /* * This function tries to determine connection status. * @@ -1647,9 +1664,20 @@ static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data) * -1 means the connection status is unknown */ CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->check_cxn(cf, data) != 0; + result = Curl_ssl->check_cxn(cf, data); CF_DATA_RESTORE(cf, save); - return result; + 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 */ } struct Curl_cftype Curl_cft_ssl = { @@ -1667,7 +1695,7 @@ struct Curl_cftype Curl_cft_ssl = { ssl_cf_cntrl, cf_ssl_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, + ssl_cf_query, }; struct Curl_cftype Curl_cft_ssl_proxy = { diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index 2e57899..ac68eab 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c @@ -94,6 +94,7 @@ struct ssl_backend_data { SSL_CTX* ctx; SSL* handle; + CURLcode io_result; /* result of last BIO cfilter operation */ }; #ifdef OPENSSL_EXTRA @@ -279,12 +280,16 @@ static long bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_OK; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + connssl->backend->io_result = result; + DEBUGF(LOG_CF(data, cf, "bio_write(len=%d) -> %zd, %d", + blen, nwritten, result)); wolfSSL_BIO_clear_retry_flags(bio); if(nwritten < 0 && CURLE_AGAIN == result) BIO_set_retry_read(bio); @@ -294,6 +299,7 @@ static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_OK; @@ -304,6 +310,9 @@ static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + connssl->backend->io_result = result; + DEBUGF(LOG_CF(data, cf, "bio_read(len=%d) -> %zd, %d", + blen, nread, result)); wolfSSL_BIO_clear_retry_flags(bio); if(nread < 0 && CURLE_AGAIN == result) BIO_set_retry_read(bio); @@ -789,6 +798,9 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } } #endif + else if(backend->io_result == CURLE_AGAIN) { + return CURLE_OK; + } else { failf(data, "SSL_connect failed with error %d: %s", detail, ERR_error_string(detail, error_buffer)); @@ -948,7 +960,6 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, ERR_clear_error(); rc = SSL_write(backend->handle, mem, memlen); - if(rc <= 0) { int err = SSL_get_error(backend->handle, rc); @@ -956,9 +967,17 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_write() */ + DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len)); *curlcode = CURLE_AGAIN; return -1; default: + if(backend->io_result == CURLE_AGAIN) { + DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len)); + *curlcode = CURLE_AGAIN; + return -1; + } + DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", + len, rc, err)); failf(data, "SSL write: %s, errno %d", ERR_error_string(err, error_buffer), SOCKERRNO); @@ -966,6 +985,7 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, return -1; } } + DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc)); return rc; } @@ -995,19 +1015,19 @@ static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) static ssize_t wolfssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, - size_t buffersize, + char *buf, size_t blen, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; + int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; int nread; DEBUGASSERT(backend); ERR_clear_error(); + *curlcode = CURLE_OK; nread = SSL_read(backend->handle, buf, buffsize); @@ -1016,22 +1036,31 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ - break; + DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen)); + *curlcode = CURLE_OK; + return 0; case SSL_ERROR_NONE: /* FALLTHROUGH */ case SSL_ERROR_WANT_READ: /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_read() */ + DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen)); *curlcode = CURLE_AGAIN; return -1; default: + if(backend->io_result == CURLE_AGAIN) { + DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen)); + *curlcode = CURLE_AGAIN; + return -1; + } failf(data, "SSL read: %s, errno %d", ERR_error_string(err, error_buffer), SOCKERRNO); *curlcode = CURLE_RECV_ERROR; return -1; } } + DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread)); return nread; } diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index 39e4fb3..c298200 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c @@ -1118,7 +1118,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, for(ccp = cert.version.beg; ccp < cert.version.end; ccp++) version = (version << 8) | *(const unsigned char *) ccp; if(data->set.ssl.certinfo) { - ccp = curl_maprintf("%lx", version); + ccp = curl_maprintf("%x", version); if(!ccp) return CURLE_OUT_OF_MEMORY; result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp); @@ -1127,7 +1127,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, return result; } if(!certnum) - infof(data, " Version: %lu (0x%lx)", version + 1, version); + infof(data, " Version: %u (0x%x)", version + 1, version); /* Serial number. */ ccp = ASN1tostr(&cert.serialNumber, 0); diff --git a/Utilities/cmcurl/lib/wildcard.c b/Utilities/cmcurl/lib/wildcard.c deleted file mode 100644 index 3b81c7a..0000000 --- a/Utilities/cmcurl/lib/wildcard.c +++ /dev/null @@ -1,75 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_FTP - -#include "wildcard.h" -#include "llist.h" -#include "fileinfo.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static void fileinfo_dtor(void *user, void *element) -{ - (void)user; - Curl_fileinfo_cleanup(element); -} - -CURLcode Curl_wildcard_init(struct WildcardData *wc) -{ - Curl_llist_init(&wc->filelist, fileinfo_dtor); - wc->state = CURLWC_INIT; - - return CURLE_OK; -} - -void Curl_wildcard_dtor(struct WildcardData *wc) -{ - if(!wc) - return; - - if(wc->dtor) { - wc->dtor(wc->protdata); - wc->dtor = ZERO_NULL; - wc->protdata = NULL; - } - DEBUGASSERT(wc->protdata == NULL); - - Curl_llist_destroy(&wc->filelist, NULL); - - - free(wc->path); - wc->path = NULL; - free(wc->pattern); - wc->pattern = NULL; - - wc->customptr = NULL; - wc->state = CURLWC_INIT; -} - -#endif /* if disabled */ diff --git a/Utilities/cmcurl/lib/wildcard.h b/Utilities/cmcurl/lib/wildcard.h deleted file mode 100644 index 9dccab0..0000000 --- a/Utilities/cmcurl/lib/wildcard.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef HEADER_CURL_WILDCARD_H -#define HEADER_CURL_WILDCARD_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_FTP -#include <curl/curl.h> -#include "llist.h" - -/* list of wildcard process states */ -typedef enum { - CURLWC_CLEAR = 0, - CURLWC_INIT = 1, - CURLWC_MATCHING, /* library is trying to get list of addresses for - downloading */ - CURLWC_DOWNLOADING, - CURLWC_CLEAN, /* deallocate resources and reset settings */ - CURLWC_SKIP, /* skip over concrete file */ - CURLWC_ERROR, /* error cases */ - CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop - will end */ -} wildcard_states; - -typedef void (*wildcard_dtor)(void *ptr); - -/* struct keeping information about wildcard download process */ -struct WildcardData { - wildcard_states state; - char *path; /* path to the directory, where we trying wildcard-match */ - char *pattern; /* wildcard pattern */ - struct Curl_llist filelist; /* llist with struct Curl_fileinfo */ - void *protdata; /* pointer to protocol specific temporary data */ - wildcard_dtor dtor; - void *customptr; /* for CURLOPT_CHUNK_DATA pointer */ -}; - -CURLcode Curl_wildcard_init(struct WildcardData *wc); -void Curl_wildcard_dtor(struct WildcardData *wc); - -struct Curl_easy; - -#else -/* FTP is disabled */ -#define Curl_wildcard_dtor(x) -#endif - -#endif /* HEADER_CURL_WILDCARD_H */ diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c index 0fc5e56..e8495dc 100644 --- a/Utilities/cmcurl/lib/ws.c +++ b/Utilities/cmcurl/lib/ws.c @@ -166,10 +166,6 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, } k->upgr101 = UPGR101_RECEIVED; - if(data->set.connect_only) - /* switch off non-blocking sockets */ - (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE); - return result; } @@ -750,9 +746,6 @@ CURLcode Curl_ws_disconnect(struct Curl_easy *data, (void)data; (void)dead_connection; Curl_dyn_free(&wsc->early); - - /* make sure this is non-blocking to avoid getting stuck in shutdown */ - (void)curlx_nonblock(conn->sock[FIRSTSOCKET], TRUE); return CURLE_OK; } |