From 1730b6861ab9ebbe9625b75c74b6fabef92bc8f5 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Wed, 11 Mar 2026 08:41:49 -0400 Subject: [PATCH 1/2] Upgrade cURL to v8.19.0 Signed-off-by: Juan Cruz Viotti --- DEPENDENCIES | 2 +- cmake/FindCURL.cmake | 13 +- vendor/curl.mask | 1 + vendor/curl/COPYING | 2 +- vendor/curl/include/curl/curl.h | 86 +- vendor/curl/include/curl/curlver.h | 6 +- vendor/curl/include/curl/easy.h | 2 +- vendor/curl/include/curl/header.h | 2 +- vendor/curl/include/curl/multi.h | 21 +- vendor/curl/include/curl/system.h | 12 +- vendor/curl/include/curl/typecheck-gcc.h | 475 +- vendor/curl/include/curl/urlapi.h | 2 +- vendor/curl/lib/altsvc.c | 42 +- vendor/curl/lib/altsvc.h | 8 +- vendor/curl/lib/amigaos.c | 1 + vendor/curl/lib/arpa_telnet.h | 34 +- vendor/curl/lib/asyn-ares.c | 189 +- vendor/curl/lib/asyn-base.c | 3 +- vendor/curl/lib/asyn-thrdd.c | 58 +- vendor/curl/lib/asyn.h | 10 +- vendor/curl/lib/bufq.h | 20 +- vendor/curl/lib/bufref.c | 4 +- vendor/curl/lib/cf-h1-proxy.c | 29 +- vendor/curl/lib/cf-h1-proxy.h | 2 +- vendor/curl/lib/cf-h2-proxy.c | 311 +- vendor/curl/lib/cf-haproxy.c | 1 - vendor/curl/lib/cf-https-connect.c | 4 +- vendor/curl/lib/cf-ip-happy.c | 36 +- vendor/curl/lib/cf-socket.c | 210 +- vendor/curl/lib/cf-socket.h | 24 +- vendor/curl/lib/cfilters.c | 160 +- vendor/curl/lib/cfilters.h | 14 +- vendor/curl/lib/config-mac.h | 10 +- vendor/curl/lib/config-os400.h | 15 +- vendor/curl/lib/config-plan9.h | 134 - vendor/curl/lib/config-riscos.h | 90 - vendor/curl/lib/config-win32.h | 82 +- vendor/curl/lib/conncache.c | 116 +- vendor/curl/lib/conncache.h | 2 +- vendor/curl/lib/connect.c | 73 +- vendor/curl/lib/connect.h | 18 +- vendor/curl/lib/content_encoding.c | 11 +- vendor/curl/lib/cookie.c | 48 +- vendor/curl/lib/cookie.h | 15 +- vendor/curl/lib/cshutdn.c | 75 +- vendor/curl/lib/cshutdn.h | 10 +- vendor/curl/lib/curl_addrinfo.c | 30 +- vendor/curl/lib/curl_addrinfo.h | 8 +- vendor/curl/lib/curl_config-cmake.h.in | 54 +- vendor/curl/lib/curl_fnmatch.c | 4 +- vendor/curl/lib/curl_fopen.c | 12 +- vendor/curl/lib/curl_get_line.c | 8 +- vendor/curl/lib/curl_gssapi.c | 6 +- vendor/curl/lib/curl_gssapi.h | 10 + vendor/curl/lib/curl_hmac.h | 4 +- vendor/curl/lib/curl_ldap.h | 12 +- vendor/curl/lib/curl_md5.h | 2 +- vendor/curl/lib/curl_ntlm_core.c | 61 +- vendor/curl/lib/curl_ntlm_core.h | 12 +- vendor/curl/lib/curl_printf.h | 4 +- vendor/curl/lib/curl_range.c | 8 +- vendor/curl/lib/curl_rtmp.c | 270 +- vendor/curl/lib/curl_rtmp.h | 13 +- vendor/curl/lib/curl_sasl.c | 31 +- vendor/curl/lib/curl_sasl.h | 6 +- vendor/curl/lib/curl_setup.h | 574 +- vendor/curl/lib/curl_setup_once.h | 346 - vendor/curl/lib/curl_sha256.h | 2 +- vendor/curl/lib/curl_sha512_256.c | 40 +- vendor/curl/lib/curl_share.c | 1 - vendor/curl/lib/curl_share.h | 14 +- vendor/curl/lib/curl_sspi.c | 2 +- vendor/curl/lib/curl_sspi.h | 12 +- vendor/curl/lib/curl_threads.h | 6 +- vendor/curl/lib/curl_trc.c | 55 +- vendor/curl/lib/curl_trc.h | 93 +- vendor/curl/lib/curlx/base64.c | 6 +- vendor/curl/lib/curlx/basename.c | 74 + .../curl/lib/curlx/{binmode.h => basename.h} | 24 +- vendor/curl/lib/curlx/curlx.h | 65 +- vendor/curl/lib/curlx/dynbuf.c | 8 +- vendor/curl/lib/curlx/fopen.c | 16 +- vendor/curl/lib/curlx/fopen.h | 39 +- vendor/curl/lib/curlx/inet_ntop.c | 10 +- vendor/curl/lib/curlx/inet_ntop.h | 4 +- vendor/curl/lib/curlx/inet_pton.c | 79 +- vendor/curl/lib/curlx/inet_pton.h | 4 +- vendor/curl/lib/curlx/multibyte.c | 4 +- vendor/curl/lib/curlx/multibyte.h | 2 +- vendor/curl/lib/curlx/nonblock.c | 4 +- .../{vquic/curl_osslq.h => curlx/snprintf.c} | 50 +- vendor/curl/lib/curlx/snprintf.h | 13 +- vendor/curl/lib/curlx/strcopy.c | 4 +- vendor/curl/lib/{ => curlx}/strdup.c | 61 +- vendor/curl/lib/{ => curlx}/strdup.h | 16 +- vendor/curl/lib/curlx/strerr.c | 22 +- vendor/curl/lib/curlx/strparse.c | 8 +- vendor/curl/lib/curlx/strparse.h | 5 +- vendor/curl/lib/curlx/timediff.c | 2 +- vendor/curl/lib/curlx/timediff.h | 2 +- vendor/curl/lib/curlx/timeval.c | 102 +- vendor/curl/lib/curlx/timeval.h | 4 +- vendor/curl/lib/curlx/version_win32.c | 51 +- vendor/curl/lib/curlx/version_win32.h | 7 +- vendor/curl/lib/curlx/wait.c | 20 +- vendor/curl/lib/curlx/wait.h | 2 +- vendor/curl/lib/curlx/warnless.c | 64 +- vendor/curl/lib/curlx/warnless.h | 2 +- vendor/curl/lib/curlx/winapi.c | 41 +- vendor/curl/lib/cw-out.c | 39 +- vendor/curl/lib/cw-pause.c | 28 +- vendor/curl/lib/dict.c | 79 +- vendor/curl/lib/dict.h | 4 +- vendor/curl/lib/doh.c | 83 +- vendor/curl/lib/doh.h | 24 +- vendor/curl/lib/dynhds.c | 5 - vendor/curl/lib/dynhds.h | 13 +- vendor/curl/lib/easy.c | 75 +- vendor/curl/lib/easy_lock.h | 4 +- vendor/curl/lib/escape.c | 2 +- vendor/curl/lib/escape.h | 2 +- vendor/curl/lib/fake_addrinfo.c | 8 +- vendor/curl/lib/fake_addrinfo.h | 4 +- vendor/curl/lib/file.c | 138 +- vendor/curl/lib/file.h | 4 +- vendor/curl/lib/formdata.c | 24 +- vendor/curl/lib/formdata.h | 2 +- vendor/curl/lib/ftp.c | 6861 +++++++++-------- vendor/curl/lib/ftp.h | 15 +- vendor/curl/lib/ftplistparser.h | 2 +- vendor/curl/lib/functypes.h | 12 +- vendor/curl/lib/getinfo.c | 11 +- vendor/curl/lib/gopher.c | 153 +- vendor/curl/lib/gopher.h | 8 +- vendor/curl/lib/hash.c | 4 +- vendor/curl/lib/hash.h | 2 +- vendor/curl/lib/headers.c | 2 +- vendor/curl/lib/headers.h | 9 +- vendor/curl/lib/hmac.c | 10 +- vendor/curl/lib/hostip.c | 267 +- vendor/curl/lib/hostip.h | 30 +- vendor/curl/lib/hostip4.c | 4 +- vendor/curl/lib/hostip6.c | 20 +- vendor/curl/lib/hsts.c | 8 +- vendor/curl/lib/hsts.h | 2 +- vendor/curl/lib/http.c | 796 +- vendor/curl/lib/http.h | 22 +- vendor/curl/lib/http1.c | 5 + vendor/curl/lib/http1.h | 2 +- vendor/curl/lib/http2.c | 579 +- vendor/curl/lib/http2.h | 7 +- vendor/curl/lib/http_aws_sigv4.c | 409 +- vendor/curl/lib/http_chunks.c | 24 +- vendor/curl/lib/http_digest.c | 2 +- vendor/curl/lib/http_negotiate.c | 10 +- vendor/curl/lib/http_proxy.c | 4 +- vendor/curl/lib/httpsrr.c | 10 +- vendor/curl/lib/idn.c | 16 +- vendor/curl/lib/idn.h | 2 +- vendor/curl/lib/if2ip.c | 2 +- vendor/curl/lib/imap.c | 1166 ++- vendor/curl/lib/imap.h | 4 +- vendor/curl/lib/ldap.c | 183 +- vendor/curl/lib/llist.h | 16 +- vendor/curl/lib/md4.c | 174 +- vendor/curl/lib/md5.c | 224 +- vendor/curl/lib/memdebug.c | 18 +- vendor/curl/lib/mime.c | 142 +- vendor/curl/lib/mime.h | 5 +- vendor/curl/lib/mprintf.c | 263 +- vendor/curl/lib/mqtt.c | 172 +- vendor/curl/lib/mqtt.h | 5 +- vendor/curl/lib/multi.c | 560 +- vendor/curl/lib/multi_ev.c | 5 +- vendor/curl/lib/multi_ntfy.c | 4 +- vendor/curl/lib/multihandle.h | 5 +- vendor/curl/lib/multiif.h | 7 +- vendor/curl/lib/netrc.c | 18 +- vendor/curl/lib/netrc.h | 8 +- vendor/curl/lib/noproxy.c | 14 +- vendor/curl/lib/openldap.c | 315 +- vendor/curl/lib/parsedate.c | 198 +- vendor/curl/lib/parsedate.h | 2 +- vendor/curl/lib/pingpong.c | 37 +- vendor/curl/lib/pingpong.h | 2 +- vendor/curl/lib/pop3.c | 847 +- vendor/curl/lib/pop3.h | 4 +- vendor/curl/lib/progress.c | 44 +- vendor/curl/lib/rand.c | 33 +- vendor/curl/lib/rand.h | 4 +- vendor/curl/lib/ratelimit.c | 238 +- vendor/curl/lib/ratelimit.h | 43 +- vendor/curl/lib/request.c | 5 +- vendor/curl/lib/request.h | 6 +- vendor/curl/lib/rtsp.c | 332 +- vendor/curl/lib/rtsp.h | 2 +- vendor/curl/lib/select.c | 11 +- vendor/curl/lib/select.h | 20 +- vendor/curl/lib/sendf.c | 188 +- vendor/curl/lib/sendf.h | 8 +- vendor/curl/lib/setopt.c | 1643 ++-- vendor/curl/lib/setopt.h | 2 +- vendor/curl/lib/setup-os400.h | 5 +- vendor/curl/lib/setup-vms.h | 13 +- vendor/curl/lib/setup-win32.h | 34 +- vendor/curl/lib/sha256.c | 100 +- vendor/curl/lib/sigpipe.h | 47 +- vendor/curl/lib/slist.c | 2 +- vendor/curl/lib/smb.c | 232 +- vendor/curl/lib/smb.h | 9 +- vendor/curl/lib/smtp.c | 1379 ++-- vendor/curl/lib/smtp.h | 4 +- vendor/curl/lib/socketpair.c | 161 +- vendor/curl/lib/socketpair.h | 50 +- vendor/curl/lib/socks.c | 47 +- vendor/curl/lib/socks_gssapi.c | 16 +- vendor/curl/lib/socks_sspi.c | 502 +- vendor/curl/lib/splay.c | 4 +- vendor/curl/lib/splay.h | 2 +- vendor/curl/lib/strcase.c | 2 +- vendor/curl/lib/strcase.h | 4 +- vendor/curl/lib/strequal.c | 18 +- vendor/curl/lib/strerror.c | 21 +- vendor/curl/lib/system_win32.c | 151 +- vendor/curl/lib/system_win32.h | 12 +- vendor/curl/lib/telnet.c | 821 +- vendor/curl/lib/telnet.h | 4 +- vendor/curl/lib/tftp.c | 755 +- vendor/curl/lib/tftp.h | 4 +- vendor/curl/lib/transfer.c | 70 +- vendor/curl/lib/uint-hash.c | 2 +- vendor/curl/lib/uint-table.h | 2 +- vendor/curl/lib/url.c | 998 ++- vendor/curl/lib/url.h | 16 +- vendor/curl/lib/urlapi-int.h | 3 + vendor/curl/lib/urlapi.c | 799 +- vendor/curl/lib/urldata.h | 250 +- vendor/curl/lib/vauth/cleartext.c | 10 +- vendor/curl/lib/vauth/cram.c | 9 +- vendor/curl/lib/vauth/digest.c | 335 +- vendor/curl/lib/vauth/digest.h | 2 +- vendor/curl/lib/vauth/digest_sspi.c | 34 +- vendor/curl/lib/vauth/gsasl.c | 6 +- vendor/curl/lib/vauth/krb5_gssapi.c | 14 +- vendor/curl/lib/vauth/krb5_sspi.c | 14 +- vendor/curl/lib/vauth/ntlm.c | 46 +- vendor/curl/lib/vauth/ntlm_sspi.c | 21 +- vendor/curl/lib/vauth/oauth2.c | 4 +- vendor/curl/lib/vauth/spnego_gssapi.c | 10 +- vendor/curl/lib/vauth/spnego_sspi.c | 14 +- vendor/curl/lib/vauth/vauth.c | 20 +- vendor/curl/lib/vauth/vauth.h | 18 +- vendor/curl/lib/version.c | 16 +- vendor/curl/lib/vquic/curl_ngtcp2.c | 294 +- vendor/curl/lib/vquic/curl_ngtcp2.h | 4 +- vendor/curl/lib/vquic/curl_osslq.c | 2458 ------ vendor/curl/lib/vquic/curl_quiche.c | 105 +- vendor/curl/lib/vquic/curl_quiche.h | 2 +- vendor/curl/lib/vquic/vquic-tls.c | 20 +- vendor/curl/lib/vquic/vquic-tls.h | 14 +- vendor/curl/lib/vquic/vquic.c | 122 +- vendor/curl/lib/vquic/vquic.h | 2 +- vendor/curl/lib/vquic/vquic_int.h | 40 +- vendor/curl/lib/vssh/libssh.c | 824 +- vendor/curl/lib/vssh/libssh2.c | 964 +-- vendor/curl/lib/vssh/ssh.h | 32 +- vendor/curl/lib/vssh/vssh.c | 151 +- vendor/curl/lib/vssh/vssh.h | 16 +- vendor/curl/lib/vtls/apple.c | 20 +- vendor/curl/lib/vtls/apple.h | 2 +- vendor/curl/lib/vtls/cipher_suite.c | 12 +- vendor/curl/lib/vtls/cipher_suite.h | 3 +- vendor/curl/lib/vtls/gtls.c | 86 +- vendor/curl/lib/vtls/gtls.h | 5 +- vendor/curl/lib/vtls/hostcheck.c | 9 +- vendor/curl/lib/vtls/hostcheck.h | 4 +- vendor/curl/lib/vtls/keylog.c | 13 +- vendor/curl/lib/vtls/keylog.h | 2 +- vendor/curl/lib/vtls/mbedtls.c | 360 +- vendor/curl/lib/vtls/mbedtls.h | 2 +- vendor/curl/lib/vtls/openssl.c | 411 +- vendor/curl/lib/vtls/openssl.h | 16 +- vendor/curl/lib/vtls/rustls.c | 40 +- vendor/curl/lib/vtls/rustls.h | 2 +- vendor/curl/lib/vtls/schannel.c | 938 ++- vendor/curl/lib/vtls/schannel.h | 48 +- vendor/curl/lib/vtls/schannel_int.h | 31 +- vendor/curl/lib/vtls/schannel_verify.c | 36 +- vendor/curl/lib/vtls/vtls.c | 128 +- vendor/curl/lib/vtls/vtls.h | 24 +- vendor/curl/lib/vtls/vtls_int.h | 24 +- vendor/curl/lib/vtls/vtls_scache.c | 511 +- vendor/curl/lib/vtls/vtls_scache.h | 8 +- vendor/curl/lib/vtls/vtls_spack.c | 16 +- vendor/curl/lib/vtls/vtls_spack.h | 2 +- vendor/curl/lib/vtls/wolfssl.c | 90 +- vendor/curl/lib/vtls/wolfssl.h | 4 +- vendor/curl/lib/vtls/x509asn1.c | 39 +- vendor/curl/lib/vtls/x509asn1.h | 6 +- vendor/curl/lib/ws.c | 124 +- vendor/curl/lib/ws.h | 11 +- 301 files changed, 16648 insertions(+), 20362 deletions(-) delete mode 100644 vendor/curl/lib/config-plan9.h delete mode 100644 vendor/curl/lib/curl_setup_once.h create mode 100644 vendor/curl/lib/curlx/basename.c rename vendor/curl/lib/curlx/{binmode.h => basename.h} (67%) rename vendor/curl/lib/{vquic/curl_osslq.h => curlx/snprintf.c} (58%) rename vendor/curl/lib/{ => curlx}/strdup.c (64%) rename vendor/curl/lib/{ => curlx}/strdup.h (75%) delete mode 100644 vendor/curl/lib/vquic/curl_osslq.c diff --git a/DEPENDENCIES b/DEPENDENCIES index 523bfee..ef32744 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,7 +1,7 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 mbedtls https://github.com/Mbed-TLS/mbedtls v3.6.5 zlib https://github.com/madler/zlib v1.3.2 -curl https://github.com/curl/curl curl-8_18_0 +curl https://github.com/curl/curl curl-8_19_0 nghttp2 https://github.com/nghttp2/nghttp2 v1.67.1 cpr https://github.com/libcpr/cpr 1.14.2 c-ares https://github.com/c-ares/c-ares v1.34.5 diff --git a/cmake/FindCURL.cmake b/cmake/FindCURL.cmake index 050e3a5..ebd965d 100644 --- a/cmake/FindCURL.cmake +++ b/cmake/FindCURL.cmake @@ -66,7 +66,7 @@ if(NOT CURL_FOUND) "${CURL_DIR}/lib/easygetopt.c" "${CURL_DIR}/lib/cf-https-connect.c" "${CURL_DIR}/lib/mime.h" - "${CURL_DIR}/lib/strdup.h" + "${CURL_DIR}/lib/curlx/strdup.h" "${CURL_DIR}/lib/fileinfo.h" "${CURL_DIR}/lib/curl_ldap.h" "${CURL_DIR}/lib/amigaos.h" @@ -99,7 +99,8 @@ if(NOT CURL_FOUND) "${CURL_DIR}/lib/tftp.c" "${CURL_DIR}/lib/curl_ntlm_core.h" "${CURL_DIR}/lib/mqtt.c" - "${CURL_DIR}/lib/config-plan9.h" + "${CURL_DIR}/lib/curlx/basename.c" + "${CURL_DIR}/lib/curlx/basename.h" "${CURL_DIR}/lib/noproxy.h" "${CURL_DIR}/lib/gopher.h" "${CURL_DIR}/lib/multihandle.h" @@ -147,13 +148,11 @@ if(NOT CURL_FOUND) "${CURL_DIR}/lib/vquic/curl_quiche.h" "${CURL_DIR}/lib/vquic/vquic.c" "${CURL_DIR}/lib/vquic/curl_ngtcp2.c" - "${CURL_DIR}/lib/vquic/curl_osslq.c" "${CURL_DIR}/lib/vquic/vquic-tls.c" "${CURL_DIR}/lib/vquic/curl_quiche.c" "${CURL_DIR}/lib/vquic/curl_ngtcp2.h" "${CURL_DIR}/lib/vquic/vquic.h" "${CURL_DIR}/lib/vquic/vquic-tls.h" - "${CURL_DIR}/lib/vquic/curl_osslq.h" "${CURL_DIR}/lib/vquic/vquic_int.h" "${CURL_DIR}/lib/cookie.h" "${CURL_DIR}/lib/uint-spbset.c" @@ -168,7 +167,7 @@ if(NOT CURL_FOUND) "${CURL_DIR}/lib/multiif.h" "${CURL_DIR}/lib/curl_endian.h" "${CURL_DIR}/lib/config-win32.h" - "${CURL_DIR}/lib/strdup.c" + "${CURL_DIR}/lib/curlx/strdup.c" "${CURL_DIR}/lib/mime.c" "${CURL_DIR}/lib/uint-bset.c" "${CURL_DIR}/lib/strequal.c" @@ -181,7 +180,7 @@ if(NOT CURL_FOUND) "${CURL_DIR}/lib/multi_ev.h" "${CURL_DIR}/lib/doh.h" "${CURL_DIR}/lib/http_negotiate.h" - "${CURL_DIR}/lib/curl_setup_once.h" + "${CURL_DIR}/lib/curlx/snprintf.c" "${CURL_DIR}/lib/curl_get_line.h" "${CURL_DIR}/lib/http_aws_sigv4.c" "${CURL_DIR}/lib/sockaddr.h" @@ -250,7 +249,6 @@ if(NOT CURL_FOUND) "${CURL_DIR}/lib/curlx/strcopy.c" "${CURL_DIR}/lib/curlx/strcopy.h" "${CURL_DIR}/lib/curlx/snprintf.h" - "${CURL_DIR}/lib/curlx/binmode.h" "${CURL_DIR}/lib/setup-vms.h" "${CURL_DIR}/lib/hostip.h" "${CURL_DIR}/lib/content_encoding.h" @@ -454,6 +452,7 @@ if(NOT CURL_FOUND) target_link_libraries(curl PRIVATE ws2_32) target_link_libraries(curl PRIVATE Crypt32) target_link_libraries(curl PRIVATE Secur32) + target_link_libraries(curl PRIVATE Iphlpapi) elseif(CMAKE_SYSTEM_NAME STREQUAL "MSYS") set(CURL_OS "\"MSYS\"") set(CURL_CA_BUNDLE "/usr/ssl/certs/ca-bundle.crt") diff --git a/vendor/curl.mask b/vendor/curl.mask index 92295ed..721ec13 100644 --- a/vendor/curl.mask +++ b/vendor/curl.mask @@ -66,3 +66,4 @@ lib/vquic/.checksrc lib/vssh/.checksrc lib/vtls/.checksrc .editorconfig +.clang-tidy.yml diff --git a/vendor/curl/COPYING b/vendor/curl/COPYING index 3fa85eb..2f71d99 100644 --- a/vendor/curl/COPYING +++ b/vendor/curl/COPYING @@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2025, Daniel Stenberg, , and many +Copyright (c) 1996 - 2026, Daniel Stenberg, , and many contributors, see the THANKS file. All rights reserved. diff --git a/vendor/curl/include/curl/curl.h b/vendor/curl/include/curl/curl.h index e755f09..209716b 100644 --- a/vendor/curl/include/curl/curl.h +++ b/vendor/curl/include/curl/curl.h @@ -59,7 +59,7 @@ #define CURL_IGNORE_DEPRECATION(statements) statements #endif -#include "curlver.h" /* libcurl version defines */ +#include "curlver.h" /* libcurl version defines */ #include "system.h" /* determine things runtime */ #include @@ -142,7 +142,7 @@ typedef SOCKET curl_socket_t; #define CURL_SOCKET_BAD INVALID_SOCKET #else typedef int curl_socket_t; -#define CURL_SOCKET_BAD -1 +#define CURL_SOCKET_BAD (-1) #endif #define curl_socket_typedef #endif /* curl_socket_typedef */ @@ -820,7 +820,7 @@ typedef enum { * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper * CURLAUTH_BEARER - HTTP Bearer token authentication * CURLAUTH_ONLY - Use together with a single other type to force no - * authentication or just that single type + * authentication or that single type * CURLAUTH_ANY - All fine types set * CURLAUTH_ANYSAFE - All fine types except Basic */ @@ -842,10 +842,13 @@ typedef enum { #define CURLAUTH_BEARER (((unsigned long)1) << 6) #define CURLAUTH_AWS_SIGV4 (((unsigned long)1) << 7) #define CURLAUTH_ONLY (((unsigned long)1) << 31) -#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) -#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE)) +#define CURLAUTH_ANY ((~CURLAUTH_DIGEST_IE) & \ + ((unsigned long)0xffffffff)) +#define CURLAUTH_ANYSAFE ((~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE)) & \ + ((unsigned long)0xffffffff)) -#define CURLSSH_AUTH_ANY ~0L /* all types supported by server */ +/* all types supported by server */ +#define CURLSSH_AUTH_ANY ((unsigned long)0xffffffff) #define CURLSSH_AUTH_NONE 0L /* none allowed, silly but complete */ #define CURLSSH_AUTH_PUBLICKEY (1L << 0) /* public/private key files */ #define CURLSSH_AUTH_PASSWORD (1L << 1) /* password */ @@ -1100,7 +1103,8 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy, #define CURLPROTO_SMBS (1L << 27) #define CURLPROTO_MQTT (1L << 28) #define CURLPROTO_GOPHERS (1L << 29) -#define CURLPROTO_ALL (~0L) /* enable everything */ +#define CURLPROTO_ALL ((unsigned long)0xffffffff) /* old-style enable + "everything" */ /* long may be 32 or 64 bits, but we should never depend on anything else but 32 */ @@ -1113,8 +1117,9 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy, /* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the string options from the header file */ -#define CURLOPT(na,t,nu) na = t + nu -#define CURLOPTDEPRECATED(na,t,nu,v,m) na CURL_DEPRECATED(v,m) = t + nu +#define CURLOPT(na, t, nu) na = ((t) + (nu)) +#define CURLOPTDEPRECATED(na, t, nu, v, m) na CURL_DEPRECATED(v, m) \ + = ((t) + (nu)) /* CURLOPT aliases that make no runtime difference */ @@ -1244,7 +1249,7 @@ typedef enum { CURLOPT(CURLOPT_QUOTE, CURLOPTTYPE_SLISTPOINT, 28), /* send FILE * or void * to store headers to, if you use a callback it - is simply passed to the callback unmodified */ + is passed to the callback unmodified */ CURLOPT(CURLOPT_HEADERDATA, CURLOPTTYPE_CBPOINT, 29), /* point to a file to read the initial cookies from, also enables @@ -1353,7 +1358,7 @@ typedef enum { /* Set the krb4/5 security level, this also enables krb4/5 awareness. This * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string - * is set but does not match one of these, 'private' will be used. */ + * is set but does not match one of these, 'private' will be used. */ CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63, 8.17.0, "removed"), @@ -2119,7 +2124,7 @@ typedef enum { /* Specify URL using CURL URL API. */ CURLOPT(CURLOPT_CURLU, CURLOPTTYPE_OBJECTPOINT, 282), - /* add trailing data just after no more data is available */ + /* add trailing data after no more data is available */ CURLOPT(CURLOPT_TRAILERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 283), /* pointer to be passed to HTTP_TRAILER_FUNCTION */ @@ -2198,7 +2203,7 @@ typedef enum { CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311), /* Function that will be called immediately before the initial request - is made on a connection (after any protocol negotiation step). */ + is made on a connection (after any protocol negotiation step). */ CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312), /* Data passed to the CURLOPT_PREREQFUNCTION callback */ @@ -2278,7 +2283,6 @@ typedef enum { #define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD #define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL -/* */ #define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT /* Added in 8.2.0 */ @@ -2352,8 +2356,8 @@ typedef enum { Unless one is set programmatically, the .netrc will be queried. */ enum CURL_NETRC_OPTION { - /* we set a single member here, just to make sure we still provide the enum, - but the values to use are defined above with L suffixes */ + /* we set a single member here, to make sure we still provide the enum, but + the values to use are defined above with L suffixes */ CURL_NETRC_LAST = 3 }; @@ -2382,7 +2386,7 @@ enum CURL_NETRC_OPTION { #define CURL_TLSAUTH_SRP 1L enum CURL_TLSAUTH { - /* we set a single member here, just to make sure we still provide the enum, + /* we set a single member here, to make sure we still provide the enum, but the values to use are defined above with L suffixes */ CURL_TLSAUTH_LAST = 2 }; @@ -2405,7 +2409,7 @@ enum CURL_TLSAUTH { #define CURL_TIMECOND_LASTMOD 3L typedef enum { - /* we set a single member here, just to make sure we still provide + /* we set a single member here, to make sure we still provide the enum typedef, but the values to use are defined above with L suffixes */ CURL_TIMECOND_LAST = 4 @@ -2704,7 +2708,7 @@ CURL_EXTERN char *curl_escape(const char *string, * * DESCRIPTION * - * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * Unescapes URL encoding in strings (converts all %XX codes to their 8-bit * versions). This function returns a new allocated string or NULL if an error * occurred. * Conversion Note: On non-ASCII platforms the ASCII %XX codes are @@ -3020,9 +3024,8 @@ typedef enum { /* Different data locks for a single share */ typedef enum { CURL_LOCK_DATA_NONE = 0, - /* CURL_LOCK_DATA_SHARE is used internally to say that - * the locking is just made to change the internal state of the share - * itself. + /* CURL_LOCK_DATA_SHARE is used internally to say that the locking is made + * to change the internal state of the share itself. */ CURL_LOCK_DATA_SHARE, CURL_LOCK_DATA_COOKIE, @@ -3096,11 +3099,10 @@ typedef enum { CURLVERSION_LAST /* never actually use this */ } CURLversion; -/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by - basically all programs ever that want to get version information. It is - meant to be a built-in version number for what kind of struct the caller - expects. If the struct ever changes, we redefine the NOW to another enum - from above. */ +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by programs + that want to get version information. It is meant to be a built-in + version number for what kind of struct the caller expects. If the struct + ever changes, we redefine the NOW to another enum from above. */ #define CURLVERSION_NOW CURLVERSION_TWELFTH struct curl_version_info_data { @@ -3183,7 +3185,8 @@ typedef struct curl_version_info_data curl_version_info_data; supported */ #define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ #define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ -#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported + (deprecated) */ #define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ #define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper is supported */ @@ -3212,7 +3215,7 @@ typedef struct curl_version_info_data curl_version_info_data; * This function returns a pointer to a static copy of the version info * struct. See above. */ -CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion stamp); /* * NAME curl_easy_strerror() @@ -3223,7 +3226,7 @@ CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ -CURL_EXTERN const char *curl_easy_strerror(CURLcode); +CURL_EXTERN const char *curl_easy_strerror(CURLcode error); /* * NAME curl_share_strerror() @@ -3234,7 +3237,7 @@ CURL_EXTERN const char *curl_easy_strerror(CURLcode); * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ -CURL_EXTERN const char *curl_share_strerror(CURLSHcode); +CURL_EXTERN const char *curl_share_strerror(CURLSHcode error); /* * NAME curl_easy_pause() @@ -3248,10 +3251,10 @@ CURL_EXTERN const char *curl_share_strerror(CURLSHcode); CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #define CURLPAUSE_RECV (1 << 0) -#define CURLPAUSE_RECV_CONT (0) +#define CURLPAUSE_RECV_CONT 0 #define CURLPAUSE_SEND (1 << 2) -#define CURLPAUSE_SEND_CONT (0) +#define CURLPAUSE_SEND_CONT 0 #define CURLPAUSE_ALL (CURLPAUSE_RECV | CURLPAUSE_SEND) #define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT | CURLPAUSE_SEND_CONT) @@ -3304,7 +3307,7 @@ CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle, #endif /* unfortunately, the easy.h and multi.h include files need options and info - stuff before they can be included! */ + stuff before they can be included! */ #include "easy.h" /* nothing in curl is fun without the easy stuff */ #include "multi.h" #include "urlapi.h" @@ -3323,11 +3326,16 @@ CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle, #if defined(__STDC__) && (__STDC__ >= 1) /* This preprocessor magic that replaces a call with the exact same call is only done to make sure application authors pass exactly three arguments - to these functions. */ -#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) -#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) -#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) -#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) + to these functions. Use recursive macros to allow using these symbols via + the C++ global namespace '::' or reusing them as method names. */ +#define curl_easy_setopt(handle, opt, param) \ + curl_easy_setopt(handle, opt, param) +#define curl_easy_getinfo(handle, info, arg) \ + curl_easy_getinfo(handle, info, arg) +#define curl_share_setopt(share, opt, param) \ + curl_share_setopt(share, opt, param) +#define curl_multi_setopt(handle, opt, param) \ + curl_multi_setopt(handle, opt, param) #endif /* __STDC__ >= 1 */ #endif /* gcc >= 4.3 && !__cplusplus && !CURL_DISABLE_TYPECHECK */ diff --git a/vendor/curl/include/curl/curlver.h b/vendor/curl/include/curl/curlver.h index 2ce526e..5ceb41a 100644 --- a/vendor/curl/include/curl/curlver.h +++ b/vendor/curl/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 "8.18.0-DEV" +#define LIBCURL_VERSION "8.19.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 18 +#define LIBCURL_VERSION_MINOR 19 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will @@ -58,7 +58,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 0x081200 +#define LIBCURL_VERSION_NUM 0x081300 /* * This is the date and time when the full source package was created. The diff --git a/vendor/curl/include/curl/easy.h b/vendor/curl/include/curl/easy.h index 5b3cdbd..0be6915 100644 --- a/vendor/curl/include/curl/easy.h +++ b/vendor/curl/include/curl/easy.h @@ -78,7 +78,7 @@ CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); * DESCRIPTION * * Re-initializes a curl handle to the default values. This puts back the - * handle to the same state as it was in when it was just created. + * handle to the same state as it was in when it was created. * * It does keep: live connections, the Session ID cache, the DNS cache and the * cookies. diff --git a/vendor/curl/include/curl/header.h b/vendor/curl/include/curl/header.h index e7334b5..85c10c8 100644 --- a/vendor/curl/include/curl/header.h +++ b/vendor/curl/include/curl/header.h @@ -31,7 +31,7 @@ extern "C" { struct curl_header { char *name; /* this might not use the same case */ char *value; - size_t amount; /* number of headers using this name */ + size_t amount; /* number of headers using this name */ size_t index; /* ... of this instance, 0 or higher */ unsigned int origin; /* see bits below */ void *anchor; /* handle privately used by libcurl */ diff --git a/vendor/curl/include/curl/multi.h b/vendor/curl/include/curl/multi.h index 531c1a9..ad6f53f 100644 --- a/vendor/curl/include/curl/multi.h +++ b/vendor/curl/include/curl/multi.h @@ -77,9 +77,8 @@ typedef enum { CURLM_LAST } CURLMcode; -/* just to make code nicer when using curl_multi_socket() you can now check - for CURLM_CALL_MULTI_SOCKET too in the same style it works for - curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +/* You can check for CURLM_CALL_MULTI_SOCKET too in the same style it works + for curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ #define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM /* bitmask bits for CURLMOPT_PIPELINING */ @@ -201,13 +200,13 @@ CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); /* * Name: curl_multi_perform() * - * Desc: When the app thinks there is data available for curl it calls this + * Desc: When the app thinks there is data available for curl it calls this * function to read/write whatever there is right now. This returns * as soon as the reads and writes are done. This function does not * require that there actually is data available for reading or that - * data can be written, it can be called just in case. It returns - * the number of handles that still transfer data in the second - * argument's integer-pointer. + * data can be written, it can be called. It returns the number of + * handles that still transfer data in the second argument's + * integer-pointer. * * Returns: CURLMcode type, general multi error code. *NOTE* that this only * returns errors etc regarding the whole multi stack. There might @@ -234,7 +233,7 @@ CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); * * Desc: Ask the multi handle if there is any messages/informationals from * the individual transfers. Messages include informationals such as - * error code from the transfer or just the fact that a transfer is + * error code from the transfer or the fact that a transfer is * completed. More details on these should be written down as well. * * Repeated calls to this function will return a new struct each @@ -269,7 +268,7 @@ CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, * * Returns: A pointer to a null-terminated error message. */ -CURL_EXTERN const char *curl_multi_strerror(CURLMcode); +CURL_EXTERN const char *curl_multi_strerror(CURLMcode error); /* * Name: curl_multi_socket() and @@ -329,7 +328,7 @@ curl_multi_socket_all(CURLM *multi_handle, int *running_handles); /* This macro below was added in 7.16.3 to push users who recompile to use * the new curl_multi_socket_action() instead of the old curl_multi_socket() */ -#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) +#define curl_multi_socket(x, y, z) curl_multi_socket_action(x, y, 0, z) #endif /* @@ -515,7 +514,7 @@ typedef int (*curl_push_callback)(CURL *parent, * * Desc: Ask curl for fds for polling. The app can use these to poll on. * We want curl_multi_perform() called as soon as one of them are - * ready. Passing zero size allows to get just a number of fds. + * ready. Passing zero size allows to get a number of fds. * * Returns: CURLMcode type, general multi error code. */ diff --git a/vendor/curl/include/curl/system.h b/vendor/curl/include/curl/system.h index a5b3e9e..064833d 100644 --- a/vendor/curl/include/curl/system.h +++ b/vendor/curl/include/curl/system.h @@ -367,20 +367,20 @@ #define CURL_PULL_SYS_POLL_H #endif -/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ -/* sys/types.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file + sys/types.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_TYPES_H # include #endif -/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ -/* sys/socket.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file + sys/socket.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_SOCKET_H # include #endif -/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file */ -/* sys/poll.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file + sys/poll.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_POLL_H # include #endif diff --git a/vendor/curl/include/curl/typecheck-gcc.h b/vendor/curl/include/curl/typecheck-gcc.h index 0642afd..d9a672e 100644 --- a/vendor/curl/include/curl/typecheck-gcc.h +++ b/vendor/curl/include/curl/typecheck-gcc.h @@ -38,191 +38,191 @@ * when compiling with -Wlogical-op. * * To add an option that uses the same type as an existing option, you will - * just need to extend the appropriate _curl_*_option macro + * need to extend the appropriate _curl_*_option macro */ #define curl_easy_setopt(handle, option, value) \ __extension__({ \ - if(__builtin_constant_p(option)) { \ - CURL_IGNORE_DEPRECATION( \ - if(curlcheck_long_option(option)) \ - if(!curlcheck_long(value)) \ - Wcurl_easy_setopt_err_long(); \ - if(curlcheck_off_t_option(option)) \ - if(!curlcheck_off_t(value)) \ - Wcurl_easy_setopt_err_curl_off_t(); \ - if(curlcheck_string_option(option)) \ - if(!curlcheck_string(value)) \ - Wcurl_easy_setopt_err_string(); \ - if((option) == CURLOPT_PRIVATE) { } \ - if(curlcheck_write_cb_option(option)) \ - if(!curlcheck_write_cb(value)) \ - Wcurl_easy_setopt_err_write_callback(); \ - if(curlcheck_curl_option(option)) \ - if(!curlcheck_curl(value)) \ - Wcurl_easy_setopt_err_curl(); \ - if((option) == CURLOPT_RESOLVER_START_FUNCTION) \ - if(!curlcheck_resolver_start_callback(value)) \ - Wcurl_easy_setopt_err_resolver_start_callback(); \ - if((option) == CURLOPT_READFUNCTION) \ - if(!curlcheck_read_cb(value)) \ - Wcurl_easy_setopt_err_read_cb(); \ - if((option) == CURLOPT_IOCTLFUNCTION) \ - if(!curlcheck_ioctl_cb(value)) \ - Wcurl_easy_setopt_err_ioctl_cb(); \ - if((option) == CURLOPT_SOCKOPTFUNCTION) \ - if(!curlcheck_sockopt_cb(value)) \ - Wcurl_easy_setopt_err_sockopt_cb(); \ - if((option) == CURLOPT_OPENSOCKETFUNCTION) \ - if(!curlcheck_opensocket_cb(value)) \ - Wcurl_easy_setopt_err_opensocket_cb(); \ - if((option) == CURLOPT_PROGRESSFUNCTION) \ - if(!curlcheck_progress_cb(value)) \ - Wcurl_easy_setopt_err_progress_cb(); \ - if((option) == CURLOPT_XFERINFOFUNCTION) \ - if(!curlcheck_xferinfo_cb(value)) \ - Wcurl_easy_setopt_err_xferinfo_cb(); \ - if((option) == CURLOPT_DEBUGFUNCTION) \ - if(!curlcheck_debug_cb(value)) \ - Wcurl_easy_setopt_err_debug_cb(); \ - if((option) == CURLOPT_SSL_CTX_FUNCTION) \ - if(!curlcheck_ssl_ctx_cb(value)) \ - Wcurl_easy_setopt_err_ssl_ctx_cb(); \ - if(curlcheck_conv_cb_option(option)) \ - if(!curlcheck_conv_cb(value)) \ - Wcurl_easy_setopt_err_conv_cb(); \ - if((option) == CURLOPT_SEEKFUNCTION) \ - if(!curlcheck_seek_cb(value)) \ - Wcurl_easy_setopt_err_seek_cb(); \ - if((option) == CURLOPT_CHUNK_BGN_FUNCTION) \ - if(!curlcheck_chunk_bgn_cb(value)) \ - Wcurl_easy_setopt_err_chunk_bgn_cb(); \ - if((option) == CURLOPT_CHUNK_END_FUNCTION) \ - if(!curlcheck_chunk_end_cb(value)) \ - Wcurl_easy_setopt_err_chunk_end_cb(); \ - if((option) == CURLOPT_CLOSESOCKETFUNCTION) \ - if(!curlcheck_close_socket_cb(value)) \ - Wcurl_easy_setopt_err_close_socket_cb(); \ - if((option) == CURLOPT_FNMATCH_FUNCTION) \ - if(!curlcheck_fnmatch_cb(value)) \ - Wcurl_easy_setopt_err_fnmatch_cb(); \ - if((option) == CURLOPT_HSTSREADFUNCTION) \ - if(!curlcheck_hstsread_cb(value)) \ - Wcurl_easy_setopt_err_hstsread_cb(); \ - if((option) == CURLOPT_HSTSWRITEFUNCTION) \ - if(!curlcheck_hstswrite_cb(value)) \ - Wcurl_easy_setopt_err_hstswrite_cb(); \ - if((option) == CURLOPT_SSH_HOSTKEYFUNCTION) \ - if(!curlcheck_ssh_hostkey_cb(value)) \ - Wcurl_easy_setopt_err_ssh_hostkey_cb(); \ - if((option) == CURLOPT_SSH_KEYFUNCTION) \ - if(!curlcheck_ssh_key_cb(value)) \ - Wcurl_easy_setopt_err_ssh_key_cb(); \ - if((option) == CURLOPT_INTERLEAVEFUNCTION) \ - if(!curlcheck_interleave_cb(value)) \ - Wcurl_easy_setopt_err_interleave_cb(); \ - if((option) == CURLOPT_PREREQFUNCTION) \ - if(!curlcheck_prereq_cb(value)) \ - Wcurl_easy_setopt_err_prereq_cb(); \ - if((option) == CURLOPT_TRAILERFUNCTION) \ - if(!curlcheck_trailer_cb(value)) \ - Wcurl_easy_setopt_err_trailer_cb(); \ - if(curlcheck_cb_data_option(option)) \ - if(!curlcheck_cb_data(value)) \ - Wcurl_easy_setopt_err_cb_data(); \ - if((option) == CURLOPT_ERRORBUFFER) \ - if(!curlcheck_error_buffer(value)) \ - Wcurl_easy_setopt_err_error_buffer(); \ - if((option) == CURLOPT_CURLU) \ - if(!curlcheck_ptr((value), CURLU)) \ - Wcurl_easy_setopt_err_curlu(); \ - if((option) == CURLOPT_STDERR) \ - if(!curlcheck_FILE(value)) \ - Wcurl_easy_setopt_err_FILE(); \ - if(curlcheck_postfields_option(option)) \ - if(!curlcheck_postfields(value)) \ - Wcurl_easy_setopt_err_postfields(); \ - if((option) == CURLOPT_HTTPPOST) \ - if(!curlcheck_arr((value), struct curl_httppost)) \ - Wcurl_easy_setopt_err_curl_httpost(); \ - if((option) == CURLOPT_MIMEPOST) \ - if(!curlcheck_ptr((value), curl_mime)) \ - Wcurl_easy_setopt_err_curl_mimepost(); \ - if(curlcheck_slist_option(option)) \ - if(!curlcheck_arr((value), struct curl_slist)) \ - Wcurl_easy_setopt_err_curl_slist(); \ - if((option) == CURLOPT_SHARE) \ - if(!curlcheck_ptr((value), CURLSH)) \ - Wcurl_easy_setopt_err_CURLSH(); \ - ) \ - } \ - curl_easy_setopt(handle, option, value); \ - }) + if(__builtin_constant_p(option)) { \ + CURL_IGNORE_DEPRECATION( \ + if(curlcheck_long_option(option)) \ + if(!curlcheck_long(value)) \ + Wcurl_easy_setopt_err_long(); \ + if(curlcheck_off_t_option(option)) \ + if(!curlcheck_off_t(value)) \ + Wcurl_easy_setopt_err_curl_off_t(); \ + if(curlcheck_string_option(option)) \ + if(!curlcheck_string(value)) \ + Wcurl_easy_setopt_err_string(); \ + if((option) == CURLOPT_PRIVATE) { } \ + if(curlcheck_write_cb_option(option)) \ + if(!curlcheck_write_cb(value)) \ + Wcurl_easy_setopt_err_write_callback(); \ + if(curlcheck_curl_option(option)) \ + if(!curlcheck_curl(value)) \ + Wcurl_easy_setopt_err_curl(); \ + if((option) == CURLOPT_RESOLVER_START_FUNCTION) \ + if(!curlcheck_resolver_start_callback(value)) \ + Wcurl_easy_setopt_err_resolver_start_callback(); \ + if((option) == CURLOPT_READFUNCTION) \ + if(!curlcheck_read_cb(value)) \ + Wcurl_easy_setopt_err_read_cb(); \ + if((option) == CURLOPT_IOCTLFUNCTION) \ + if(!curlcheck_ioctl_cb(value)) \ + Wcurl_easy_setopt_err_ioctl_cb(); \ + if((option) == CURLOPT_SOCKOPTFUNCTION) \ + if(!curlcheck_sockopt_cb(value)) \ + Wcurl_easy_setopt_err_sockopt_cb(); \ + if((option) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!curlcheck_opensocket_cb(value)) \ + Wcurl_easy_setopt_err_opensocket_cb(); \ + if((option) == CURLOPT_PROGRESSFUNCTION) \ + if(!curlcheck_progress_cb(value)) \ + Wcurl_easy_setopt_err_progress_cb(); \ + if((option) == CURLOPT_XFERINFOFUNCTION) \ + if(!curlcheck_xferinfo_cb(value)) \ + Wcurl_easy_setopt_err_xferinfo_cb(); \ + if((option) == CURLOPT_DEBUGFUNCTION) \ + if(!curlcheck_debug_cb(value)) \ + Wcurl_easy_setopt_err_debug_cb(); \ + if((option) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!curlcheck_ssl_ctx_cb(value)) \ + Wcurl_easy_setopt_err_ssl_ctx_cb(); \ + if(curlcheck_conv_cb_option(option)) \ + if(!curlcheck_conv_cb(value)) \ + Wcurl_easy_setopt_err_conv_cb(); \ + if((option) == CURLOPT_SEEKFUNCTION) \ + if(!curlcheck_seek_cb(value)) \ + Wcurl_easy_setopt_err_seek_cb(); \ + if((option) == CURLOPT_CHUNK_BGN_FUNCTION) \ + if(!curlcheck_chunk_bgn_cb(value)) \ + Wcurl_easy_setopt_err_chunk_bgn_cb(); \ + if((option) == CURLOPT_CHUNK_END_FUNCTION) \ + if(!curlcheck_chunk_end_cb(value)) \ + Wcurl_easy_setopt_err_chunk_end_cb(); \ + if((option) == CURLOPT_CLOSESOCKETFUNCTION) \ + if(!curlcheck_close_socket_cb(value)) \ + Wcurl_easy_setopt_err_close_socket_cb(); \ + if((option) == CURLOPT_FNMATCH_FUNCTION) \ + if(!curlcheck_fnmatch_cb(value)) \ + Wcurl_easy_setopt_err_fnmatch_cb(); \ + if((option) == CURLOPT_HSTSREADFUNCTION) \ + if(!curlcheck_hstsread_cb(value)) \ + Wcurl_easy_setopt_err_hstsread_cb(); \ + if((option) == CURLOPT_HSTSWRITEFUNCTION) \ + if(!curlcheck_hstswrite_cb(value)) \ + Wcurl_easy_setopt_err_hstswrite_cb(); \ + if((option) == CURLOPT_SSH_HOSTKEYFUNCTION) \ + if(!curlcheck_ssh_hostkey_cb(value)) \ + Wcurl_easy_setopt_err_ssh_hostkey_cb(); \ + if((option) == CURLOPT_SSH_KEYFUNCTION) \ + if(!curlcheck_ssh_key_cb(value)) \ + Wcurl_easy_setopt_err_ssh_key_cb(); \ + if((option) == CURLOPT_INTERLEAVEFUNCTION) \ + if(!curlcheck_interleave_cb(value)) \ + Wcurl_easy_setopt_err_interleave_cb(); \ + if((option) == CURLOPT_PREREQFUNCTION) \ + if(!curlcheck_prereq_cb(value)) \ + Wcurl_easy_setopt_err_prereq_cb(); \ + if((option) == CURLOPT_TRAILERFUNCTION) \ + if(!curlcheck_trailer_cb(value)) \ + Wcurl_easy_setopt_err_trailer_cb(); \ + if(curlcheck_cb_data_option(option)) \ + if(!curlcheck_cb_data(value)) \ + Wcurl_easy_setopt_err_cb_data(); \ + if((option) == CURLOPT_ERRORBUFFER) \ + if(!curlcheck_error_buffer(value)) \ + Wcurl_easy_setopt_err_error_buffer(); \ + if((option) == CURLOPT_CURLU) \ + if(!curlcheck_ptr((value), CURLU)) \ + Wcurl_easy_setopt_err_curlu(); \ + if((option) == CURLOPT_STDERR) \ + if(!curlcheck_FILE(value)) \ + Wcurl_easy_setopt_err_FILE(); \ + if(curlcheck_postfields_option(option)) \ + if(!curlcheck_postfields(value)) \ + Wcurl_easy_setopt_err_postfields(); \ + if((option) == CURLOPT_HTTPPOST) \ + if(!curlcheck_arr((value), struct curl_httppost)) \ + Wcurl_easy_setopt_err_curl_httpost(); \ + if((option) == CURLOPT_MIMEPOST) \ + if(!curlcheck_ptr((value), curl_mime)) \ + Wcurl_easy_setopt_err_curl_mimepost(); \ + if(curlcheck_slist_option(option)) \ + if(!curlcheck_arr((value), struct curl_slist)) \ + Wcurl_easy_setopt_err_curl_slist(); \ + if((option) == CURLOPT_SHARE) \ + if(!curlcheck_ptr((value), CURLSH)) \ + Wcurl_easy_setopt_err_CURLSH(); \ + ) \ + } \ + (curl_easy_setopt)(handle, option, value); \ + }) /* wraps curl_easy_getinfo() with type checking */ #define curl_easy_getinfo(handle, info, arg) \ __extension__({ \ - if(__builtin_constant_p(info)) { \ - CURL_IGNORE_DEPRECATION( \ - if(curlcheck_string_info(info)) \ - if(!curlcheck_arr((arg), char *)) \ - Wcurl_easy_getinfo_err_string(); \ - if(curlcheck_long_info(info)) \ - if(!curlcheck_arr((arg), long)) \ - Wcurl_easy_getinfo_err_long(); \ - if(curlcheck_double_info(info)) \ - if(!curlcheck_arr((arg), double)) \ - Wcurl_easy_getinfo_err_double(); \ - if(curlcheck_slist_info(info)) \ - if(!curlcheck_arr((arg), struct curl_slist *)) \ - Wcurl_easy_getinfo_err_curl_slist(); \ - if(curlcheck_tlssessioninfo_info(info)) \ - if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ - Wcurl_easy_getinfo_err_curl_tlssessioninfo(); \ - if(curlcheck_certinfo_info(info)) \ - if(!curlcheck_arr((arg), struct curl_certinfo *)) \ - Wcurl_easy_getinfo_err_curl_certinfo(); \ - if(curlcheck_socket_info(info)) \ - if(!curlcheck_arr((arg), curl_socket_t)) \ - Wcurl_easy_getinfo_err_curl_socket(); \ - if(curlcheck_off_t_info(info)) \ - if(!curlcheck_arr((arg), curl_off_t)) \ - Wcurl_easy_getinfo_err_curl_off_t(); \ - ) \ - } \ - curl_easy_getinfo(handle, info, arg); \ - }) + if(__builtin_constant_p(info)) { \ + CURL_IGNORE_DEPRECATION( \ + if(curlcheck_string_info(info)) \ + if(!curlcheck_arr((arg), char *)) \ + Wcurl_easy_getinfo_err_string(); \ + if(curlcheck_long_info(info)) \ + if(!curlcheck_arr((arg), long)) \ + Wcurl_easy_getinfo_err_long(); \ + if(curlcheck_double_info(info)) \ + if(!curlcheck_arr((arg), double)) \ + Wcurl_easy_getinfo_err_double(); \ + if(curlcheck_slist_info(info)) \ + if(!curlcheck_arr((arg), struct curl_slist *)) \ + Wcurl_easy_getinfo_err_curl_slist(); \ + if(curlcheck_tlssessioninfo_info(info)) \ + if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ + Wcurl_easy_getinfo_err_curl_tlssessioninfo(); \ + if(curlcheck_certinfo_info(info)) \ + if(!curlcheck_arr((arg), struct curl_certinfo *)) \ + Wcurl_easy_getinfo_err_curl_certinfo(); \ + if(curlcheck_socket_info(info)) \ + if(!curlcheck_arr((arg), curl_socket_t)) \ + Wcurl_easy_getinfo_err_curl_socket(); \ + if(curlcheck_off_t_info(info)) \ + if(!curlcheck_arr((arg), curl_off_t)) \ + Wcurl_easy_getinfo_err_curl_off_t(); \ + ) \ + } \ + (curl_easy_getinfo)(handle, info, arg); \ + }) #define curl_multi_setopt(handle, option, value) \ __extension__({ \ - if(__builtin_constant_p(option)) { \ - if(curlcheck_long_option(option)) \ - if(!curlcheck_long(value)) \ - Wcurl_multi_setopt_err_long(); \ - if(curlcheck_off_t_option(option)) \ - if(!curlcheck_off_t(value)) \ - Wcurl_multi_setopt_err_curl_off_t(); \ - if(curlcheck_multicb_data_option(option)) \ - if(!curlcheck_cb_data(value)) \ - Wcurl_multi_setopt_err_cb_data(); \ - if(curlcheck_charpp_option(option)) \ - if(!curlcheck_ptrptr(value, char)) \ - Wcurl_multi_setopt_err_charpp(); \ - if((option) == CURLMOPT_NOTIFYFUNCTION) \ - if(!curlcheck_multinotify_cb(value)) \ - Wcurl_multi_setopt_err_notifycb(); \ - if((option) == CURLMOPT_PUSHFUNCTION) \ - if(!curlcheck_multipush_cb(value)) \ - Wcurl_multi_setopt_err_pushcb(); \ - if((option) == CURLMOPT_SOCKETFUNCTION) \ - if(!curlcheck_multisocket_cb(value)) \ - Wcurl_multi_setopt_err_socketcb(); \ - if((option) == CURLMOPT_TIMERFUNCTION) \ - if(!curlcheck_multitimer_cb(value)) \ - Wcurl_multi_setopt_err_timercb(); \ - } \ - curl_multi_setopt(handle, option, value); \ - }) + if(__builtin_constant_p(option)) { \ + if(curlcheck_long_option(option)) \ + if(!curlcheck_long(value)) \ + Wcurl_multi_setopt_err_long(); \ + if(curlcheck_off_t_option(option)) \ + if(!curlcheck_off_t(value)) \ + Wcurl_multi_setopt_err_curl_off_t(); \ + if(curlcheck_multicb_data_option(option)) \ + if(!curlcheck_cb_data(value)) \ + Wcurl_multi_setopt_err_cb_data(); \ + if(curlcheck_charpp_option(option)) \ + if(!curlcheck_ptrptr(value, char)) \ + Wcurl_multi_setopt_err_charpp(); \ + if((option) == CURLMOPT_NOTIFYFUNCTION) \ + if(!curlcheck_multinotify_cb(value)) \ + Wcurl_multi_setopt_err_notifycb(); \ + if((option) == CURLMOPT_PUSHFUNCTION) \ + if(!curlcheck_multipush_cb(value)) \ + Wcurl_multi_setopt_err_pushcb(); \ + if((option) == CURLMOPT_SOCKETFUNCTION) \ + if(!curlcheck_multisocket_cb(value)) \ + Wcurl_multi_setopt_err_socketcb(); \ + if((option) == CURLMOPT_TIMERFUNCTION) \ + if(!curlcheck_multitimer_cb(value)) \ + Wcurl_multi_setopt_err_timercb(); \ + } \ + (curl_multi_setopt)(handle, option, value); \ + }) /* evaluates to true if the option takes a data argument to pass to a callback */ @@ -260,9 +260,10 @@ curlcheck_cb_compatible((expr), curl_notify_callback)) /* - * For now, just make sure that the functions are called with three arguments + * Make sure that the functions are called with three arguments */ -#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_share_setopt(share, opt, param) \ + (curl_share_setopt)(share, opt, param) /* the actual warnings, triggered by calling the Wcurl_easy_setopt_err* * functions */ @@ -511,82 +512,84 @@ CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) /* evaluates to true if option takes a data argument to pass to a callback */ -#define curlcheck_cb_data_option(option) \ - ((option) == CURLOPT_CHUNK_DATA || \ - (option) == CURLOPT_CLOSESOCKETDATA || \ - (option) == CURLOPT_DEBUGDATA || \ - (option) == CURLOPT_FNMATCH_DATA || \ - (option) == CURLOPT_HEADERDATA || \ - (option) == CURLOPT_HSTSREADDATA || \ - (option) == CURLOPT_HSTSWRITEDATA || \ - (option) == CURLOPT_INTERLEAVEDATA || \ - (option) == CURLOPT_IOCTLDATA || \ - (option) == CURLOPT_OPENSOCKETDATA || \ - (option) == CURLOPT_PREREQDATA || \ - (option) == CURLOPT_XFERINFODATA || \ - (option) == CURLOPT_READDATA || \ - (option) == CURLOPT_SEEKDATA || \ - (option) == CURLOPT_SOCKOPTDATA || \ - (option) == CURLOPT_SSH_KEYDATA || \ - (option) == CURLOPT_SSL_CTX_DATA || \ - (option) == CURLOPT_WRITEDATA || \ - (option) == CURLOPT_RESOLVER_START_DATA || \ - (option) == CURLOPT_TRAILERDATA || \ - (option) == CURLOPT_SSH_HOSTKEYDATA || \ +#define curlcheck_cb_data_option(option) \ + ((option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_CLOSESOCKETDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_HSTSREADDATA || \ + (option) == CURLOPT_HSTSWRITEDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PREREQDATA || \ + (option) == CURLOPT_XFERINFODATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_WRITEDATA || \ + (option) == CURLOPT_RESOLVER_START_DATA || \ + (option) == CURLOPT_TRAILERDATA || \ + (option) == CURLOPT_SSH_HOSTKEYDATA || \ 0) /* evaluates to true if option takes a POST data argument (void* or char*) */ -#define curlcheck_postfields_option(option) \ - ((option) == CURLOPT_POSTFIELDS || \ - (option) == CURLOPT_COPYPOSTFIELDS || \ +#define curlcheck_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ 0) /* evaluates to true if option takes a struct curl_slist * argument */ -#define curlcheck_slist_option(option) \ - ((option) == CURLOPT_HTTP200ALIASES || \ - (option) == CURLOPT_HTTPHEADER || \ - (option) == CURLOPT_MAIL_RCPT || \ - (option) == CURLOPT_POSTQUOTE || \ - (option) == CURLOPT_PREQUOTE || \ - (option) == CURLOPT_PROXYHEADER || \ - (option) == CURLOPT_QUOTE || \ - (option) == CURLOPT_RESOLVE || \ - (option) == CURLOPT_TELNETOPTIONS || \ - (option) == CURLOPT_CONNECT_TO || \ +#define curlcheck_slist_option(option) \ + ((option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_MAIL_RCPT || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_PROXYHEADER || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_RESOLVE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + (option) == CURLOPT_CONNECT_TO || \ 0) /* groups of curl_easy_getinfo infos that take the same type of argument */ /* evaluates to true if info expects a pointer to char * argument */ -#define curlcheck_string_info(info) \ - (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG && \ +#define curlcheck_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG && \ (info) != CURLINFO_PRIVATE) /* evaluates to true if info expects a pointer to long argument */ -#define curlcheck_long_info(info) \ +#define curlcheck_long_info(info) \ (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) /* evaluates to true if info expects a pointer to double argument */ -#define curlcheck_double_info(info) \ +#define curlcheck_double_info(info) \ (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) /* true if info expects a pointer to struct curl_slist * argument */ #define curlcheck_slist_info(info) \ - (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST)) + (((info) == CURLINFO_SSL_ENGINES) || \ + ((info) == CURLINFO_COOKIELIST)) /* true if info expects a pointer to struct curl_tlssessioninfo * argument */ -#define curlcheck_tlssessioninfo_info(info) \ - (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION)) +#define curlcheck_tlssessioninfo_info(info) \ + (((info) == CURLINFO_TLS_SSL_PTR) || \ + ((info) == CURLINFO_TLS_SESSION)) /* true if info expects a pointer to struct curl_certinfo * argument */ #define curlcheck_certinfo_info(info) ((info) == CURLINFO_CERTINFO) /* true if info expects a pointer to struct curl_socket_t argument */ -#define curlcheck_socket_info(info) \ +#define curlcheck_socket_info(info) \ (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T) /* true if info expects a pointer to curl_off_t argument */ -#define curlcheck_off_t_info(info) \ +#define curlcheck_off_t_info(info) \ (CURLINFO_OFF_T < (info)) /* @@ -603,7 +606,7 @@ CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, */ /* XXX: should evaluate to true if expr is a pointer */ -#define curlcheck_any_ptr(expr) \ +#define curlcheck_any_ptr(expr) \ (sizeof(expr) == sizeof(void *)) /* evaluates to true if expr is NULL */ @@ -667,7 +670,7 @@ CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, ) /* evaluates to true if expr is of type curl_off_t */ -#define curlcheck_off_t(expr) \ +#define curlcheck_off_t(expr) \ (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) /* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ @@ -705,8 +708,8 @@ CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, __builtin_types_compatible_p(__typeof__(func) *, type)) /* evaluates to true if expr is of type curl_resolver_start_callback */ -#define curlcheck_resolver_start_callback(expr) \ - (curlcheck_NULL(expr) || \ +#define curlcheck_resolver_start_callback(expr) \ + (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_resolver_start_callback)) /* evaluates to true if expr is of type curl_read_callback or "similar" */ @@ -740,11 +743,11 @@ typedef size_t (*Wcurl_read_callback6)(void *, size_t, size_t, FILE *); curlcheck_cb_compatible((expr), Wcurl_write_callback6)) typedef size_t (*Wcurl_write_callback1)(const char *, size_t, size_t, void *); typedef size_t (*Wcurl_write_callback2)(const char *, size_t, size_t, - const void *); + const void *); typedef size_t (*Wcurl_write_callback3)(const char *, size_t, size_t, FILE *); typedef size_t (*Wcurl_write_callback4)(const void *, size_t, size_t, void *); typedef size_t (*Wcurl_write_callback5)(const void *, size_t, size_t, - const void *); + const void *); typedef size_t (*Wcurl_write_callback6)(const void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ @@ -768,7 +771,7 @@ typedef curlioerr (*Wcurl_ioctl_callback4)(CURL *, curliocmd, const void *); curlcheck_cb_compatible((expr), Wcurl_sockopt_callback2)) typedef int (*Wcurl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); typedef int (*Wcurl_sockopt_callback2)(const void *, curl_socket_t, - curlsocktype); + curlsocktype); /* evaluates to true if expr is of type curl_opensocket_callback or "similar" */ @@ -816,21 +819,21 @@ typedef int (*Wcurl_progress_callback2)(const void *, curlcheck_cb_compatible((expr), Wcurl_debug_callback6) || \ curlcheck_cb_compatible((expr), Wcurl_debug_callback7) || \ curlcheck_cb_compatible((expr), Wcurl_debug_callback8)) -typedef int (*Wcurl_debug_callback1) (CURL *, +typedef int (*Wcurl_debug_callback1)(CURL *, curl_infotype, char *, size_t, void *); -typedef int (*Wcurl_debug_callback2) (CURL *, +typedef int (*Wcurl_debug_callback2)(CURL *, curl_infotype, char *, size_t, const void *); -typedef int (*Wcurl_debug_callback3) (CURL *, +typedef int (*Wcurl_debug_callback3)(CURL *, curl_infotype, const char *, size_t, void *); -typedef int (*Wcurl_debug_callback4) (CURL *, +typedef int (*Wcurl_debug_callback4)(CURL *, curl_infotype, const char *, size_t, const void *); -typedef int (*Wcurl_debug_callback5) (CURL *, +typedef int (*Wcurl_debug_callback5)(CURL *, curl_infotype, unsigned char *, size_t, void *); -typedef int (*Wcurl_debug_callback6) (CURL *, +typedef int (*Wcurl_debug_callback6)(CURL *, curl_infotype, unsigned char *, size_t, const void *); -typedef int (*Wcurl_debug_callback7) (CURL *, +typedef int (*Wcurl_debug_callback7)(CURL *, curl_infotype, const unsigned char *, size_t, void *); -typedef int (*Wcurl_debug_callback8) (CURL *, +typedef int (*Wcurl_debug_callback8)(CURL *, curl_infotype, const unsigned char *, size_t, const void *); /* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ diff --git a/vendor/curl/include/curl/urlapi.h b/vendor/curl/include/curl/urlapi.h index dabe44a..f7a42b3 100644 --- a/vendor/curl/include/curl/urlapi.h +++ b/vendor/curl/include/curl/urlapi.h @@ -146,7 +146,7 @@ CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, * readable error string. This is useful for printing meaningful error * messages. */ -CURL_EXTERN const char *curl_url_strerror(CURLUcode); +CURL_EXTERN const char *curl_url_strerror(CURLUcode error); #ifdef __cplusplus } /* end of extern "C" */ diff --git a/vendor/curl/lib/altsvc.c b/vendor/curl/lib/altsvc.c index 7006333..34da3e1 100644 --- a/vendor/curl/lib/altsvc.c +++ b/vendor/curl/lib/altsvc.c @@ -39,7 +39,7 @@ #include "connect.h" #define MAX_ALTSVC_LINE 4095 -#define MAX_ALTSVC_DATELEN 256 +#define MAX_ALTSVC_DATELEN 17 #define MAX_ALTSVC_HOSTLEN 2048 #define MAX_ALTSVC_ALPNLEN 10 @@ -175,7 +175,7 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line) (size_t)srcport, (size_t)dstport); if(as) { as->expires = expires; - as->prio = 0; /* not supported to just set zero */ + as->prio = 0; /* not supported, set zero */ as->persist = persist ? 1 : 0; Curl_llist_append(&asi->list, as, &as->node); } @@ -229,7 +229,6 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) /* * Write this single altsvc entry to a single output line */ - static CURLcode altsvc_out(struct altsvc *as, FILE *fp) { struct tm stamp; @@ -310,10 +309,18 @@ CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file) /* * Curl_altsvc_ctrl() passes on the external bitmask. */ -CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl) +CURLcode Curl_altsvc_ctrl(struct Curl_easy *data, const long ctrl) { - DEBUGASSERT(asi); - asi->flags = ctrl; + DEBUGASSERT(data); + if(!ctrl) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(!data->asi) { + data->asi = Curl_altsvc_init(); + if(!data->asi) + return CURLE_OUT_OF_MEMORY; + } + data->asi->flags = ctrl; return CURLE_OK; } @@ -321,12 +328,12 @@ CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl) * Curl_altsvc_cleanup() frees an altsvc cache instance and all associated * resources. */ -void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp) +void Curl_altsvc_cleanup(struct altsvcinfo **asi) { - if(*altsvcp) { + if(*asi) { struct Curl_llist_node *e; struct Curl_llist_node *n; - struct altsvcinfo *altsvc = *altsvcp; + struct altsvcinfo *altsvc = *asi; for(e = Curl_llist_head(&altsvc->list); e; e = n) { struct altsvc *as = Curl_node_elem(e); n = Curl_node_next(e); @@ -334,7 +341,7 @@ void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp) } curlx_free(altsvc->filename); curlx_free(altsvc); - *altsvcp = NULL; /* clear the pointer */ + *asi = NULL; /* clear the pointer */ } } @@ -342,21 +349,21 @@ void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp) * Curl_altsvc_save() writes the altsvc cache to a file. */ CURLcode Curl_altsvc_save(struct Curl_easy *data, - struct altsvcinfo *altsvc, const char *file) + struct altsvcinfo *asi, const char *file) { CURLcode result = CURLE_OK; FILE *out; char *tempstore = NULL; - if(!altsvc) + if(!asi) /* no cache activated */ return CURLE_OK; /* if not new name is given, use the one we stored from the load */ - if(!file && altsvc->filename) - file = altsvc->filename; + if(!file && asi->filename) + file = asi->filename; - if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) + if((asi->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) /* marked as read-only, no file or zero length filename */ return CURLE_OK; @@ -367,7 +374,7 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data, fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n" "# This file was generated by libcurl! Edit at your own risk.\n", out); - for(e = Curl_llist_head(&altsvc->list); e; e = n) { + for(e = Curl_llist_head(&asi->list); e; e = n) { struct altsvc *as = Curl_node_elem(e); n = Curl_node_next(e); result = altsvc_out(as, out); @@ -459,9 +466,6 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, unsigned short dstport = srcport; /* the same by default */ size_t entries = 0; struct Curl_str alpn; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif DEBUGASSERT(asi); diff --git a/vendor/curl/lib/altsvc.h b/vendor/curl/lib/altsvc.h index b82eaa8..2931af4 100644 --- a/vendor/curl/lib/altsvc.h +++ b/vendor/curl/lib/altsvc.h @@ -54,11 +54,11 @@ struct altsvcinfo *Curl_altsvc_init(void); CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file); CURLcode Curl_altsvc_save(struct Curl_easy *data, struct altsvcinfo *asi, const char *file); -CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl); -void Curl_altsvc_cleanup(struct altsvcinfo **altsvc); +CURLcode Curl_altsvc_ctrl(struct Curl_easy *data, const long ctrl); +void Curl_altsvc_cleanup(struct altsvcinfo **asi); CURLcode Curl_altsvc_parse(struct Curl_easy *data, - struct altsvcinfo *altsvc, const char *value, - enum alpnid srcalpn, const char *srchost, + struct altsvcinfo *asi, const char *value, + enum alpnid srcalpnid, const char *srchost, unsigned short srcport); bool Curl_altsvc_lookup(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost, diff --git a/vendor/curl/lib/amigaos.c b/vendor/curl/lib/amigaos.c index 7ce5045..d2afdbf 100644 --- a/vendor/curl/lib/amigaos.c +++ b/vendor/curl/lib/amigaos.c @@ -26,6 +26,7 @@ #ifdef __AMIGA__ #include "hostip.h" +#include "curl_addrinfo.h" #include "amigaos.h" #ifdef HAVE_PROTO_BSDSOCKET_H diff --git a/vendor/curl/lib/arpa_telnet.h b/vendor/curl/lib/arpa_telnet.h index daf2487..b5faab4 100644 --- a/vendor/curl/lib/arpa_telnet.h +++ b/vendor/curl/lib/arpa_telnet.h @@ -27,19 +27,18 @@ /* * Telnet option defines. Add more here if in need. */ -#define CURL_TELOPT_BINARY 0 /* binary 8bit data */ -#define CURL_TELOPT_ECHO 1 /* just echo! */ -#define CURL_TELOPT_SGA 3 /* Suppress Go Ahead */ -#define CURL_TELOPT_EXOPL 255 /* EXtended OPtions List */ -#define CURL_TELOPT_TTYPE 24 /* Terminal TYPE */ -#define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */ -#define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */ - +#define CURL_TELOPT_BINARY 0 /* binary 8-bit data */ +#define CURL_TELOPT_ECHO 1 /* echo */ +#define CURL_TELOPT_SGA 3 /* Suppress Go Ahead */ +#define CURL_TELOPT_EXOPL 255 /* EXtended OPtions List */ +#define CURL_TELOPT_TTYPE 24 /* Terminal TYPE */ +#define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */ +#define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */ #define CURL_TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */ #define CURL_NEW_ENV_VAR 0 #define CURL_NEW_ENV_VALUE 1 -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE /* * The telnet options represented as strings */ @@ -55,9 +54,9 @@ static const char * const telnetoptions[] = { "TERM SPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON" }; -#define CURL_TELOPT(x) telnetoptions[x] +#define CURL_TELOPT(x) telnetoptions[x] #else -#define CURL_TELOPT(x) "" +#define CURL_TELOPT(x) "" #endif #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON @@ -81,7 +80,7 @@ static const char * const telnetoptions[] = { #define CURL_DONT 254 /* DO NOT use this option! */ #define CURL_IAC 255 /* Interpret As Command */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE /* * Then those numbers represented as strings: */ @@ -101,13 +100,14 @@ static const char * const telnetcmds[] = { #define CURL_TELQUAL_INFO 2 #define CURL_TELQUAL_NAME 3 -#define CURL_TELCMD_OK(x) (((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \ - ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM)) +#define CURL_TELCMD_OK(x) \ + (((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \ + ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM)) -#ifndef CURL_DISABLE_VERBOSE_STRINGS -#define CURL_TELCMD(x) telnetcmds[(x) - CURL_TELCMD_MINIMUM] +#ifdef CURLVERBOSE +#define CURL_TELCMD(x) telnetcmds[(x) - CURL_TELCMD_MINIMUM] #else -#define CURL_TELCMD(x) "" +#define CURL_TELCMD(x) "" #endif #endif /* CURL_DISABLE_TELNET */ diff --git a/vendor/curl/lib/asyn-ares.c b/vendor/curl/lib/asyn-ares.c index 7544140..4d063d9 100644 --- a/vendor/curl/lib/asyn-ares.c +++ b/vendor/curl/lib/asyn-ares.c @@ -47,6 +47,7 @@ #include "urldata.h" #include "cfilters.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" #include "url.h" @@ -62,11 +63,6 @@ #include /* really old c-ares did not include this by itself */ -#if ARES_VERSION >= 0x010601 -/* IPv6 supported since 1.6.1 */ -#define HAVE_CARES_IPV6 1 -#endif - #if ARES_VERSION >= 0x010704 #define HAVE_CARES_SERVERS_CSV 1 #define HAVE_CARES_LOCAL_DEV 1 @@ -308,7 +304,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, (ares->happy_eyeballs_dns_time.tv_sec || ares->happy_eyeballs_dns_time.tv_usec) && (curlx_ptimediff_ms(Curl_pgrs_now(data), - &ares->happy_eyeballs_dns_time) >= + &ares->happy_eyeballs_dns_time) >= HAPPY_EYEBALLS_DNS_TIMEOUT)) { /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer running. */ @@ -331,13 +327,15 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, result = ares->result; if(ares->ares_status == ARES_SUCCESS && !result) { data->state.async.dns = - Curl_dnscache_mk_entry(data, ares->temp_ai, + Curl_dnscache_mk_entry(data, &ares->temp_ai, data->state.async.hostname, 0, data->state.async.port, FALSE); - if(data->state.async.dns) - ares->temp_ai = NULL; /* temp_ai now owned by entry */ + if(!data->state.async.dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } #ifdef HTTPSRR_WORKS - if(data->state.async.dns) { + { struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo); if(!lhrr) result = CURLE_OUT_OF_MEMORY; @@ -345,7 +343,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, data->state.async.dns->hinfo = lhrr; } #endif - if(!result && data->state.async.dns) + if(!result) result = Curl_dnscache_add(data, data->state.async.dns); } /* if we have not found anything, report the proper @@ -370,6 +368,32 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, return result; } +static timediff_t async_ares_poll_timeout(struct async_ares_ctx *ares, + timediff_t timeout_ms) +{ + struct timeval *ares_calced, time_buf, max_timeout; + int itimeout_ms; + +#if TIMEDIFF_T_MAX > INT_MAX + itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : + ((timeout_ms < 0) ? -1 : (int)timeout_ms); +#else + itimeout_ms = (int)timeout_ms; +#endif + max_timeout.tv_sec = itimeout_ms / 1000; + max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; + + /* c-ares tells us the shortest timeout of any operation on channel */ + ares_calced = ares_timeout(ares->channel, &max_timeout, &time_buf); + /* use the timeout period ares returned to us above if less than one + second is left, otherwise use 1000ms to make sure the progress callback + gets called frequent enough */ + if(!ares_calced->tv_sec) + return (timediff_t)(ares_calced->tv_usec / 1000); + else + return 1000; +} + /* * Curl_async_await() * @@ -382,76 +406,53 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. */ CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **entry) + struct Curl_dns_entry **dns) { struct async_ares_ctx *ares = &data->state.async.ares; + struct curltime start = *Curl_pgrs_now(data); CURLcode result = CURLE_OK; - timediff_t timeout_ms; - - DEBUGASSERT(entry); - *entry = NULL; /* clear on entry */ - timeout_ms = Curl_timeleft_ms(data, TRUE); - if(timeout_ms < 0) { - /* already expired! */ - connclose(data->conn, "Timed out before name resolve started"); - return CURLE_OPERATION_TIMEDOUT; - } - if(!timeout_ms) - timeout_ms = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve */ + DEBUGASSERT(dns); + *dns = NULL; /* clear on entry */ - /* Wait for the name resolve query to complete. */ + /* Wait for the name resolve query to complete or time out. */ while(!result) { - struct timeval *real_timeout, time_buf, max_timeout; - int itimeout_ms; - timediff_t call_timeout_ms; - -#if TIMEDIFF_T_MAX > INT_MAX - itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : (int)timeout_ms; -#else - itimeout_ms = (int)timeout_ms; -#endif + timediff_t timeout_ms; - max_timeout.tv_sec = itimeout_ms / 1000; - max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; - - real_timeout = ares_timeout(ares->channel, &max_timeout, &time_buf); + timeout_ms = Curl_timeleft_ms(data); + if(!timeout_ms) { /* no applicable timeout from `data`*/ + timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &start); + if(elapsed_ms < (CURL_TIMEOUT_RESOLVE * 1000)) + timeout_ms = (CURL_TIMEOUT_RESOLVE * 1000) - elapsed_ms; + else + timeout_ms = -1; + } - /* use the timeout period ares returned to us above if less than one - second is left, otherwise just use 1000ms to make sure the progress - callback gets called frequent enough */ - if(!real_timeout->tv_sec) - call_timeout_ms = (timediff_t)(real_timeout->tv_usec / 1000); - else - call_timeout_ms = 1000; + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } - if(Curl_ares_perform(ares->channel, call_timeout_ms) < 0) - return CURLE_UNRECOVERABLE_POLL; + if(Curl_ares_perform(ares->channel, + async_ares_poll_timeout(ares, timeout_ms)) < 0) { + result = CURLE_UNRECOVERABLE_POLL; + break; + } - result = Curl_async_is_resolved(data, entry); + result = Curl_async_is_resolved(data, dns); if(result || data->state.async.done) break; - if(Curl_pgrsUpdate(data)) + if(Curl_pgrsUpdate(data)) { result = CURLE_ABORTED_BY_CALLBACK; - else { - struct curltime now = curlx_now(); /* update in loop */ - timediff_t elapsed_ms = curlx_ptimediff_ms(&now, Curl_pgrs_now(data)); - if(elapsed_ms <= 0) - timeout_ms -= 1; /* always deduct at least 1 */ - else if(elapsed_ms > timeout_ms) - timeout_ms = -1; - else - timeout_ms -= elapsed_ms; + break; } - if(timeout_ms < 0) - result = CURLE_OPERATION_TIMEDOUT; } /* Operation complete, if the lookup was successful we now have the entry in the cache. */ data->state.async.done = TRUE; - *entry = data->state.async.dns; + *dns = data->state.async.dns; if(result) ares_cancel(ares->channel); @@ -546,12 +547,11 @@ static void async_ares_hostbyname_cb(void *user_data, talking to a pool of DNS servers that can only successfully resolve IPv4 address, for example). - it is also possible that the other request could always just take - longer because it needs more time or only the second DNS server can - fulfill it successfully. But, to align with the philosophy of Happy - Eyeballs, we do not want to wait _too_ long or users will think - requests are slow when IPv6 lookups do not actually work (but IPv4 - ones do). + it is also possible that the other request could always take longer + because it needs more time or only the second DNS server can fulfill it + successfully. But, to align with the philosophy of Happy Eyeballs, we + do not want to wait _too_ long or users will think requests are slow + when IPv6 lookups do not actually work (but IPv4 ones do). So, now that we have a usable answer (some IPv4 addresses, some IPv6 addresses, or "no such domain"), we start a timeout for the remaining @@ -559,24 +559,23 @@ static void async_ares_hostbyname_cb(void *user_data, request came back quickly, that need not be the case. It might be that this completing request did not get a result from the first DNS server or even the first round of the whole DNS server pool. So it - could already be quite some time after we issued the DNS queries in + could already be a long time after we issued the DNS queries in the first place. Without modifying c-ares, we cannot know exactly where in its retry cycle we are. We could guess based on how much time has gone by, but it does not really matter. Happy Eyeballs tells - us that, given usable information in hand, we simply do not want to + us that, given usable information in hand, we do not want to wait "too much longer" after we get a result. - We simply wait an additional amount of time equal to the default - c-ares query timeout. That is enough time for a typical parallel - response to arrive without being "too long". Even on a network - where one of the two types of queries is failing or timing out - constantly, this will usually mean we wait a total of the default - c-ares timeout (5 seconds) plus the round trip time for the successful - request, which seems bearable. The downside is that c-ares might race - with us to issue one more retry just before we give up, but it seems - better to "waste" that request instead of trying to guess the perfect - timeout to prevent it. After all, we do not even know where in the - c-ares retry cycle each request is. + We wait an additional amount of time equal to the default c-ares + query timeout. That is enough time for a typical parallel response to + arrive without being "too long". Even on a network where one of the two + types of queries is failing or timing out constantly, this will usually + mean we wait a total of the default c-ares timeout (5 seconds) plus the + round trip time for the successful request, which seems bearable. The + downside is that c-ares might race with us to issue one more retry + before we give up, but it seems better to "waste" that request instead + of trying to guess the perfect timeout to prevent it. After all, we do + not even know where in the c-ares retry cycle each request is. */ ares->happy_eyeballs_dns_time = *Curl_pgrs_now(data); Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS); @@ -602,8 +601,8 @@ async_ares_node2addr(struct ares_addrinfo_node *node) for(ai = node; ai != NULL; ai = ai->ai_next) { size_t ss_size; struct Curl_addrinfo *ca; - /* ignore elements with unsupported address family, */ - /* settle family-specific sockaddr structure size. */ + /* ignore elements with unsupported address family, + settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); #ifdef USE_IPV6 @@ -627,8 +626,8 @@ async_ares_node2addr(struct ares_addrinfo_node *node) break; } - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ + /* copy each structure member individually, member ordering, + size, or padding might be different for each platform. */ ca->ai_flags = ai->ai_flags; ca->ai_family = ai->ai_family; @@ -739,8 +738,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, ares->ares_status = ARES_ENOTFOUND; ares->result = CURLE_OK; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) && \ - ARES_VERSION >= 0x011800 /* >= v1.24.0 */ +#if defined(CURLVERBOSE) && ARES_VERSION >= 0x011800 /* >= v1.24.0 */ if(CURL_TRC_DNS_is_verbose(data)) { char *csv = ares_get_servers_csv(ares->channel); CURL_TRC_DNS(data, "asyn-ares: servers=%s", csv); @@ -755,8 +753,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, int pf = PF_INET; memset(&hints, 0, sizeof(hints)); #ifdef CURLRES_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && - Curl_ipv6works(data)) { + if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { /* The stack seems to be IPv6-enabled */ if(ip_version == CURL_IPRESOLVE_V6) pf = PF_INET6; @@ -782,7 +779,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, } #else -#ifdef HAVE_CARES_IPV6 +#if ARES_VERSION >= 0x010601 /* IPv6 supported since 1.6.1 */ if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { /* The stack seems to be IPv6-enabled */ /* areschannel is already setup in the Curl_open() function */ @@ -836,7 +833,7 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, const char *servers = data->set.str[STRING_DNS_SERVERS]; int ares_result = ARES_SUCCESS; -#if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV) +#if defined(DEBUGBUILD) && defined(HAVE_CARES_SERVERS_CSV) if(getenv("CURL_DNS_SERVER")) servers = getenv("CURL_DNS_SERVER"); #endif @@ -849,7 +846,7 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, } #ifdef HAVE_CARES_SERVERS_CSV - /* if channel is not there, this is just a parameter check */ + /* if channel is not there, this is a parameter check */ if(ares->channel) #ifdef HAVE_CARES_PORTS_CSV ares_result = ares_set_servers_ports_csv(ares->channel, servers); @@ -892,7 +889,7 @@ CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data) if(!interf) interf = ""; - /* if channel is not there, this is just a parameter check */ + /* if channel is not there, this is a parameter check */ if(ares->channel) ares_set_local_dev(ares->channel, interf); @@ -911,7 +908,7 @@ CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data) struct in_addr a4; const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4]; - if((!local_ip4) || (local_ip4[0] == 0)) { + if(!local_ip4 || (local_ip4[0] == 0)) { a4.s_addr = 0; /* disabled: do not bind to a specific address */ } else { @@ -921,7 +918,7 @@ CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data) } } - /* if channel is not there yet, this is just a parameter check */ + /* if channel is not there yet, this is a parameter check */ if(ares->channel) ares_set_local_ip4(ares->channel, ntohl(a4.s_addr)); @@ -940,7 +937,7 @@ CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) unsigned char a6[INET6_ADDRSTRLEN]; const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6]; - if((!local_ip6) || (local_ip6[0] == 0)) { + if(!local_ip6 || (local_ip6[0] == 0)) { /* disabled: do not bind to a specific address */ memset(a6, 0, sizeof(a6)); } @@ -951,7 +948,7 @@ CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) } } - /* if channel is not there, this is just a parameter check */ + /* if channel is not there, this is a parameter check */ if(ares->channel) ares_set_local_ip6(ares->channel, a6); diff --git a/vendor/curl/lib/asyn-base.c b/vendor/curl/lib/asyn-base.c index 02797f1..44bf98c 100644 --- a/vendor/curl/lib/asyn-base.c +++ b/vendor/curl/lib/asyn-base.c @@ -43,7 +43,6 @@ #endif #include "urldata.h" -#include "asyn.h" #include "hostip.h" #include "multiif.h" #include "select.h" @@ -156,7 +155,7 @@ int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms) nfds = 0; if(!nfds) - /* Call ares_process() unconditionally here, even if we simply timed out + /* Call ares_process() unconditionally here, even if we timed out above, as otherwise the ares name resolve will not timeout! */ ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); else { diff --git a/vendor/curl/lib/asyn-thrdd.c b/vendor/curl/lib/asyn-thrdd.c index 690e813..c3ed8ea 100644 --- a/vendor/curl/lib/asyn-thrdd.c +++ b/vendor/curl/lib/asyn-thrdd.c @@ -56,8 +56,10 @@ #include "urldata.h" #include "cfilters.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" +#include "httpsrr.h" #include "url.h" #include "multiif.h" #include "curl_threads.h" @@ -99,8 +101,8 @@ void Curl_async_global_cleanup(void) #endif } -static void async_thrdd_destroy(struct Curl_easy *); -static void async_thrdd_shutdown(struct Curl_easy *); +static void async_thrdd_destroy(struct Curl_easy *data); +static void async_thrdd_shutdown(struct Curl_easy *data); CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) { @@ -116,7 +118,6 @@ static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx, struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx; bool destroy; - (void)data; if(!addr_ctx) return; @@ -134,12 +135,7 @@ static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx, curlx_free(addr_ctx->hostname); if(addr_ctx->res) Curl_freeaddrinfo(addr_ctx->res); -#ifndef CURL_DISABLE_SOCKETPAIR -#ifndef USE_EVENTFD - wakeup_close(addr_ctx->sock_pair[1]); -#endif - wakeup_close(addr_ctx->sock_pair[0]); -#endif + Curl_wakeup_destroy(addr_ctx->sock_pair); curlx_free(addr_ctx); } *paddr_ctx = NULL; @@ -170,7 +166,7 @@ addr_ctx_create(struct Curl_easy *data, #ifndef CURL_DISABLE_SOCKETPAIR /* create socket pair or pipe */ - if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) { + if(Curl_wakeup_init(addr_ctx->sock_pair, FALSE) < 0) { addr_ctx->sock_pair[0] = CURL_SOCKET_BAD; addr_ctx->sock_pair[1] = CURL_SOCKET_BAD; goto err_exit; @@ -203,7 +199,7 @@ addr_ctx_create(struct Curl_easy *data, static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg) { struct async_thrdd_addr_ctx *addr_ctx = arg; - bool do_abort; + curl_bit do_abort; Curl_mutex_acquire(&addr_ctx->mutx); do_abort = addr_ctx->do_abort; @@ -232,15 +228,11 @@ static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg) Curl_mutex_release(&addr_ctx->mutx); #ifndef CURL_DISABLE_SOCKETPAIR if(!do_abort) { -#ifdef USE_EVENTFD - const uint64_t buf[1] = { 1 }; -#else - const char buf[1] = { 1 }; -#endif /* Thread is done, notify transfer */ - if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) { + int err = Curl_wakeup_signal(addr_ctx->sock_pair); + if(err) { /* update sock_error to errno */ - addr_ctx->sock_error = SOCKERRNO; + addr_ctx->sock_error = err; } } #endif @@ -277,15 +269,10 @@ static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg) Curl_mutex_release(&addr_ctx->mutx); #ifndef CURL_DISABLE_SOCKETPAIR if(!do_abort) { -#ifdef USE_EVENTFD - const uint64_t buf[1] = { 1 }; -#else - const char buf[1] = { 1 }; -#endif - /* Thread is done, notify transfer */ - if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) { + int err = Curl_wakeup_signal(addr_ctx->sock_pair); + if(err) { /* update sock_error to errno */ - addr_ctx->sock_error = SOCKERRNO; + addr_ctx->sock_error = err; } } #endif @@ -314,7 +301,7 @@ static void async_thrdd_destroy(struct Curl_easy *data) #endif if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) { - bool done; + curl_bit done; Curl_mutex_acquire(&addr->mutx); #ifndef CURL_DISABLE_SOCKETPAIR @@ -373,7 +360,7 @@ static CURLcode async_rr_start(struct Curl_easy *data, int port) curlx_free(rrname); return CURLE_FAILED_INIT; } -#ifdef CURLDEBUG +#ifdef DEBUGBUILD if(getenv("CURL_DNS_SERVER")) { const char *servers = getenv("CURL_DNS_SERVER"); status = ares_set_servers_ports_csv(thrdd->rr.channel, servers); @@ -475,7 +462,7 @@ static void async_thrdd_shutdown(struct Curl_easy *data) { struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr; - bool done; + curl_bit done; if(!addr_ctx) return; @@ -534,7 +521,7 @@ static CURLcode asyn_thrdd_await(struct Curl_easy *data, /* * Until we gain a way to signal the resolver threads to stop early, we must - * simply wait for them and ignore their results. + * wait for them and ignore their results. */ void Curl_async_thrdd_shutdown(struct Curl_easy *data) { @@ -565,11 +552,11 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data) * This is the version for resolves-in-a-thread. */ CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **entry) + struct Curl_dns_entry **dns) { struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; if(thrdd->addr) - return asyn_thrdd_await(data, thrdd->addr, entry); + return asyn_thrdd_await(data, thrdd->addr, dns); return CURLE_FAILED_INIT; } @@ -582,7 +569,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **dns) { struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - bool done = FALSE; + curl_bit done = FALSE; DEBUGASSERT(dns); *dns = NULL; @@ -617,10 +604,9 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, if(thrdd->addr->res) { data->state.async.dns = - Curl_dnscache_mk_entry(data, thrdd->addr->res, + Curl_dnscache_mk_entry(data, &thrdd->addr->res, data->state.async.hostname, 0, data->state.async.port, FALSE); - thrdd->addr->res = NULL; if(!data->state.async.dns) result = CURLE_OUT_OF_MEMORY; @@ -679,7 +665,7 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) { struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; CURLcode result = CURLE_OK; - bool thrd_done; + curl_bit thrd_done; #if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR) (void)ps; diff --git a/vendor/curl/lib/asyn.h b/vendor/curl/lib/asyn.h index b961081..68628f9 100644 --- a/vendor/curl/lib/asyn.h +++ b/vendor/curl/lib/asyn.h @@ -25,15 +25,17 @@ ***************************************************************************/ #include "curl_setup.h" +#if defined(USE_HTTPSRR) && defined(USE_ARES) +#include "httpsrr.h" +#endif + struct Curl_easy; struct Curl_dns_entry; #ifdef CURLRES_ASYNCH #include "curl_addrinfo.h" -#include "httpsrr.h" -struct addrinfo; struct hostent; struct connectdata; struct easy_pollset; @@ -68,7 +70,7 @@ void Curl_async_global_cleanup(void); * Get the resolver implementation instance (c-ares channel) or NULL * for passing to application callback. */ -CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl); +CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl); /* Curl_async_pollset() * @@ -104,7 +106,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. */ CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **dnsentry); + struct Curl_dns_entry **dns); /* * Curl_async_getaddrinfo() - when using this resolver diff --git a/vendor/curl/lib/bufq.h b/vendor/curl/lib/bufq.h index bcd7df2..8dd186f 100644 --- a/vendor/curl/lib/bufq.h +++ b/vendor/curl/lib/bufq.h @@ -44,9 +44,8 @@ struct buf_chunk { /** * A pool for providing/keeping a number of chunks of the same size * - * The same pool can be shared by many `bufq` instances. However, a pool - * is not thread safe. All bufqs using it are supposed to operate in the - * same thread. + * The same pool can be shared by many `bufq` instances. A pool is not thread + * safe. All bufqs using it are supposed to operate in the same thread. */ struct bufc_pool { struct buf_chunk *spare; /* list of available spare chunks */ @@ -77,10 +76,10 @@ void Curl_bufcp_free(struct bufc_pool *pool); * * By default, writing to a full bufq will return (-1, CURLE_AGAIN). Same * as reading from an empty bufq. - * With `BUFQ_OPT_SOFT_LIMIT` set, a bufq will allow writing becond this - * limit and use more than `max_chunks`. However it will report that it - * is full nevertheless. This is provided for situation where writes - * preferably never fail (except for memory exhaustion). + * With `BUFQ_OPT_SOFT_LIMIT` set, a bufq will allow writing beyond this limit + * and use more than `max_chunks`. It will report that it is full + * nevertheless. This is provided for situation where writes preferably never + * fail (except for memory exhaustion). * * By default and without a pool, a bufq will keep chunks that read * empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will @@ -104,7 +103,7 @@ struct bufq { * Default behaviour: chunk limit is "hard", meaning attempts to write * more bytes than can be hold in `max_chunks` is refused and will return * -1, CURLE_AGAIN. */ -#define BUFQ_OPT_NONE (0) +#define BUFQ_OPT_NONE 0 /** * Make `max_chunks` a "soft" limit. A bufq will report that it is "full" * when `max_chunks` are used, but allows writing beyond this limit. @@ -197,9 +196,8 @@ bool Curl_bufq_peek_at(struct bufq *q, size_t offset, const uint8_t **pbuf, size_t *plen); /** - * Tell the buffer queue to discard `amount` buf bytes at the head - * of the queue. Skipping more buf than is currently buffered will - * just empty the queue. + * Tell the buffer queue to discard `amount` buf bytes at the head of the + * queue. Skipping more buf than is currently buffered will empty the queue. */ void Curl_bufq_skip(struct bufq *q, size_t amount); diff --git a/vendor/curl/lib/bufref.c b/vendor/curl/lib/bufref.c index b1272d5..50fafda 100644 --- a/vendor/curl/lib/bufref.c +++ b/vendor/curl/lib/bufref.c @@ -25,7 +25,7 @@ #include "urldata.h" #include "bufref.h" -#include "strdup.h" +#include "curlx/strdup.h" #ifdef DEBUGBUILD #define SIGNATURE 0x5c48e9b2 /* Random pattern. */ @@ -128,7 +128,7 @@ CURLcode Curl_bufref_memdup0(struct bufref *br, const void *ptr, size_t len) DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH); if(ptr) { - cpy = Curl_memdup0(ptr, len); + cpy = curlx_memdup0(ptr, len); if(!cpy) return CURLE_OUT_OF_MEMORY; } diff --git a/vendor/curl/lib/cf-h1-proxy.c b/vendor/curl/lib/cf-h1-proxy.c index c41a68d..69e050b 100644 --- a/vendor/curl/lib/cf-h1-proxy.c +++ b/vendor/curl/lib/cf-h1-proxy.c @@ -31,7 +31,6 @@ #include "http.h" #include "http1.h" #include "http_proxy.h" -#include "url.h" #include "select.h" #include "progress.h" #include "cfilters.h" @@ -39,7 +38,6 @@ #include "connect.h" #include "curl_trc.h" #include "strcase.h" -#include "transfer.h" #include "curlx/strparse.h" @@ -106,8 +104,8 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf, { struct h1_tunnel_state *ts; - if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) { - failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme); + if(cf->conn->scheme->flags & PROTOPT_NOTCPPROXY) { + failf(data, "%s cannot be done over CONNECT", cf->conn->scheme->name); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -208,9 +206,8 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, int http_minor; CURLcode result; - /* This only happens if we have looped here due to authentication - reasons, and we do not really use the newly cloned URL here - then. Just free it. */ + /* This only happens if we have looped here due to authentication reasons, + and we do not really use the newly cloned URL here then. Free it. */ Curl_safefree(data->req.newurl); result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); @@ -241,7 +238,7 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, struct h1_tunnel_state *ts, bool *done) { - uint8_t *buf = curlx_dyn_uptr(&ts->request_data); + const uint8_t *buf = curlx_dyn_uptr(&ts->request_data); size_t request_len = curlx_dyn_len(&ts->request_data); size_t blen = request_len; CURLcode result = CURLE_OK; @@ -262,7 +259,7 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, DEBUGASSERT(blen >= nwritten); ts->nsent += nwritten; - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf, nwritten); + Curl_debug(data, CURLINFO_HEADER_OUT, (const char *)buf, nwritten); out: if(result) @@ -342,8 +339,8 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && !ISDIGIT(header[12])) { /* store the HTTP code from the proxy */ - data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 + - (header[10] - '0') * 10 + (header[11] - '0'); + data->info.httpproxycode = k->httpcode = ((header[9] - '0') * 100) + + ((header[10] - '0') * 10) + (header[11] - '0'); } return result; } @@ -353,9 +350,9 @@ static CURLcode single_header(struct Curl_cfilter *cf, struct h1_tunnel_state *ts) { CURLcode result = CURLE_OK; - char *linep = curlx_dyn_ptr(&ts->rcvbuf); + const char *linep = curlx_dyn_ptr(&ts->rcvbuf); size_t line_len = curlx_dyn_len(&ts->rcvbuf); /* bytes in this line */ - struct SingleRequest *k = &data->req; + const struct SingleRequest *k = &data->req; int writetype; ts->headerlines++; @@ -470,7 +467,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* This means we are currently ignoring a response-body */ if(ts->cl) { - /* A Content-Length based body: simply count down the counter + /* A Content-Length based body: count down the counter and make sure to break out of the loop when we are done! */ ts->cl--; if(ts->cl <= 0) { @@ -531,7 +528,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, if(byte != 0x0a) continue; else { - char *linep = curlx_dyn_ptr(&ts->rcvbuf); + const char *linep = curlx_dyn_ptr(&ts->rcvbuf); size_t hlen = curlx_dyn_len(&ts->rcvbuf); if(hlen && ISNEWLINE(linep[0])) { /* end of headers */ @@ -571,7 +568,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, do { - if(Curl_timeleft_ms(data, TRUE) < 0) { + if(Curl_timeleft_ms(data) < 0) { failf(data, "Proxy CONNECT aborted due to timeout"); result = CURLE_OPERATION_TIMEDOUT; goto out; diff --git a/vendor/curl/lib/cf-h1-proxy.h b/vendor/curl/lib/cf-h1-proxy.h index e48d827..6544ec5 100644 --- a/vendor/curl/lib/cf-h1-proxy.h +++ b/vendor/curl/lib/cf-h1-proxy.h @@ -27,7 +27,7 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) -CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf, +CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data); extern struct Curl_cftype Curl_cft_h1_proxy; diff --git a/vendor/curl/lib/cf-h2-proxy.c b/vendor/curl/lib/cf-h2-proxy.c index 4f101a0..4275e1e 100644 --- a/vendor/curl/lib/cf-h2-proxy.c +++ b/vendor/curl/lib/cf-h2-proxy.c @@ -226,8 +226,7 @@ static CURLcode proxy_h2_nw_out_writer(void *writer_ctx, if(cf) { struct Curl_easy *data = CF_DATA_CURRENT(cf); CURLcode result; - result = Curl_conn_cf_send(cf->next, data, buf, buflen, - FALSE, pnwritten); + result = Curl_conn_cf_send(cf->next, data, buf, buflen, FALSE, pnwritten); CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %d, %zu", buflen, result, *pnwritten); return result; @@ -259,113 +258,6 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf, return rc; } -static ssize_t on_session_send(nghttp2_session *h2, - const uint8_t *buf, size_t blen, - int flags, void *userp); -static int proxy_h2_on_frame_recv(nghttp2_session *session, - const nghttp2_frame *frame, - void *userp); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int proxy_h2_on_frame_send(nghttp2_session *session, - const nghttp2_frame *frame, - void *userp); -#endif -static int proxy_h2_on_stream_close(nghttp2_session *session, - int32_t stream_id, - uint32_t error_code, void *userp); -static int proxy_h2_on_header(nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp); -static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *mem, size_t len, void *userp); - -/* - * Initialize the cfilter context - */ -static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OUT_OF_MEMORY; - nghttp2_session_callbacks *cbs = NULL; - int rc; - - DEBUGASSERT(!ctx->h2); - memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); - - Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); - Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); - - if(tunnel_stream_init(cf, &ctx->tunnel)) - goto out; - - rc = nghttp2_session_callbacks_new(&cbs); - if(rc) { - failf(data, "Could not initialize nghttp2 callbacks"); - goto out; - } - - nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); - nghttp2_session_callbacks_set_on_frame_recv_callback( - cbs, proxy_h2_on_frame_recv); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - nghttp2_session_callbacks_set_on_frame_send_callback(cbs, - proxy_h2_on_frame_send); -#endif - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - cbs, tunnel_recv_callback); - nghttp2_session_callbacks_set_on_stream_close_callback( - cbs, proxy_h2_on_stream_close); - nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); - - /* The nghttp2 session is not yet setup, do it */ - rc = proxy_h2_client_new(cf, cbs); - if(rc) { - failf(data, "Could not initialize nghttp2"); - goto out; - } - - { - nghttp2_settings_entry iv[3]; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = Curl_multi_max_concurrent_streams(data->multi); - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = H2_TUNNEL_WINDOW_SIZE; - iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = 0; - rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); - if(rc) { - failf(data, "nghttp2_submit_settings() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - } - - rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, - PROXY_HTTP2_HUGE_WINDOW_SIZE); - if(rc) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - - /* all set, traffic will be send on connect */ - result = CURLE_OK; - -out: - if(cbs) - nghttp2_session_callbacks_del(cbs); - CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); - return result; -} - static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx) { return !nghttp2_session_want_read(ctx->h2) && @@ -379,7 +271,6 @@ static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, size_t nwritten; CURLcode result; - (void)data; if(Curl_bufq_is_empty(&ctx->outbufq)) return CURLE_OK; @@ -530,82 +421,11 @@ static ssize_t on_session_send(nghttp2_session *h2, if(!nwritten) return NGHTTP2_ERR_WOULDBLOCK; - return (nwritten > SSIZE_MAX) ? + return (nwritten > SSIZE_MAX) ? NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int proxy_h2_fr_print(const nghttp2_frame *frame, - char *buffer, size_t blen) -{ - switch(frame->hd.type) { - case NGHTTP2_DATA: { - return curl_msnprintf(buffer, blen, - "FRAME[DATA, len=%d, eos=%d, padlen=%d]", - (int)frame->hd.length, - !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), - (int)frame->data.padlen); - } - case NGHTTP2_HEADERS: { - return curl_msnprintf(buffer, blen, - "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", - (int)frame->hd.length, - !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), - !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); - } - case NGHTTP2_PRIORITY: { - return curl_msnprintf(buffer, blen, - "FRAME[PRIORITY, len=%d, flags=%d]", - (int)frame->hd.length, frame->hd.flags); - } - case NGHTTP2_RST_STREAM: { - return curl_msnprintf(buffer, blen, - "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", - (int)frame->hd.length, frame->hd.flags, - frame->rst_stream.error_code); - } - case NGHTTP2_SETTINGS: { - if(frame->hd.flags & NGHTTP2_FLAG_ACK) { - return curl_msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); - } - return curl_msnprintf(buffer, blen, - "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); - } - case NGHTTP2_PUSH_PROMISE: - return curl_msnprintf(buffer, blen, - "FRAME[PUSH_PROMISE, len=%d, hend=%d]", - (int)frame->hd.length, - !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); - case NGHTTP2_PING: - return curl_msnprintf(buffer, blen, - "FRAME[PING, len=%d, ack=%d]", - (int)frame->hd.length, - frame->hd.flags & NGHTTP2_FLAG_ACK); - case NGHTTP2_GOAWAY: { - char scratch[128]; - size_t s_len = CURL_ARRAYSIZE(scratch); - size_t len = (frame->goaway.opaque_data_len < s_len) ? - frame->goaway.opaque_data_len : s_len-1; - if(len) - memcpy(scratch, frame->goaway.opaque_data, len); - scratch[len] = '\0'; - return curl_msnprintf(buffer, blen, - "FRAME[GOAWAY, error=%d, reason='%s', " - "last_stream=%d]", frame->goaway.error_code, - scratch, frame->goaway.last_stream_id); - } - case NGHTTP2_WINDOW_UPDATE: { - return curl_msnprintf(buffer, blen, - "FRAME[WINDOW_UPDATE, incr=%d]", - frame->window_update.window_size_increment); - } - default: - return curl_msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", - frame->hd.type, (int)frame->hd.length, - frame->hd.flags); - } -} - +#ifdef CURLVERBOSE static int proxy_h2_on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, void *userp) @@ -615,16 +435,16 @@ static int proxy_h2_on_frame_send(nghttp2_session *session, (void)session; DEBUGASSERT(data); - if(data && Curl_trc_cf_is_verbose(cf, data)) { + if(Curl_trc_cf_is_verbose(cf, data)) { char buffer[256]; int len; - len = proxy_h2_fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_fr_print(frame, buffer, sizeof(buffer) - 1); buffer[len] = 0; CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); } return 0; } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ static int proxy_h2_on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, @@ -637,15 +457,15 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, (void)session; DEBUGASSERT(data); -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_cf_is_verbose(cf, data)) { char buffer[256]; int len; - len = proxy_h2_fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_fr_print(frame, buffer, sizeof(buffer) - 1); buffer[len] = 0; CURL_TRC_CF(data, cf, "[%d] <- %s", frame->hd.stream_id, buffer); } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ @@ -696,6 +516,10 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, drain_tunnel(cf, data, &ctx->tunnel); } break; + case NGHTTP2_RST_STREAM: + if(frame->rst_stream.error_code) + ctx->tunnel.reset = TRUE; + break; default: break; } @@ -716,7 +540,6 @@ static int proxy_h2_on_header(nghttp2_session *session, CURLcode result; (void)flags; - (void)data; (void)session; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ if(stream_id != ctx->tunnel.stream_id) { @@ -785,7 +608,6 @@ static ssize_t tunnel_send_callback(nghttp2_session *session, size_t nread; (void)source; - (void)data; (void)ctx; if(!stream_id) @@ -849,7 +671,6 @@ static int proxy_h2_on_stream_close(nghttp2_session *session, struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)session; - (void)data; if(stream_id != ctx->tunnel.stream_id) return 0; @@ -858,6 +679,8 @@ static int proxy_h2_on_stream_close(nghttp2_session *session, stream_id, nghttp2_http2_strerror(error_code), error_code); ctx->tunnel.closed = TRUE; ctx->tunnel.error = error_code; + if(error_code) + ctx->tunnel.reset = TRUE; return 0; } @@ -1055,6 +878,89 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, return result; } +/* + * Initialize the cfilter context + */ +static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + nghttp2_session_callbacks *cbs = NULL; + int rc; + + DEBUGASSERT(!ctx->h2); + memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); + + Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); + Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); + + if(tunnel_stream_init(cf, &ctx->tunnel)) + goto out; + + rc = nghttp2_session_callbacks_new(&cbs); + if(rc) { + failf(data, "Could not initialize nghttp2 callbacks"); + goto out; + } + + nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); + nghttp2_session_callbacks_set_on_frame_recv_callback( + cbs, proxy_h2_on_frame_recv); +#ifdef CURLVERBOSE + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, + proxy_h2_on_frame_send); +#endif + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + cbs, tunnel_recv_callback); + nghttp2_session_callbacks_set_on_stream_close_callback( + cbs, proxy_h2_on_stream_close); + nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); + + /* The nghttp2 session is not yet setup, do it */ + rc = proxy_h2_client_new(cf, cbs); + if(rc) { + failf(data, "Could not initialize nghttp2"); + goto out; + } + + { + nghttp2_settings_entry iv[3]; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = Curl_multi_max_concurrent_streams(data->multi); + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = H2_TUNNEL_WINDOW_SIZE; + iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[2].value = 0; + rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); + if(rc) { + failf(data, "nghttp2_submit_settings() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + } + + rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, + PROXY_HTTP2_HUGE_WINDOW_SIZE); + if(rc) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + /* all set, traffic will be send on connect */ + result = CURLE_OK; + +out: + if(cbs) + nghttp2_session_callbacks_del(cbs); + CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); + return result; +} + static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1086,7 +992,7 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, } DEBUGASSERT(ts->authority); - if(Curl_timeleft_ms(data, TRUE) < 0) { + if(Curl_timeleft_ms(data) < 0) { failf(data, "Proxy CONNECT aborted due to timeout"); result = CURLE_OPERATION_TIMEDOUT; goto out; @@ -1251,20 +1157,11 @@ static CURLcode h2_handle_tunnel_close(struct Curl_cfilter *cf, struct cf_h2_proxy_ctx *ctx = cf->ctx; *pnread = 0; - if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { - CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " - "connection", ctx->tunnel.stream_id); - failf(data, "proxy server refused HTTP/2 stream"); - return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - } - else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { - failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", - ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), - ctx->tunnel.error); - return CURLE_HTTP2_STREAM; - } - else if(ctx->tunnel.reset) { - failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); + if(ctx->tunnel.error) { + failf(data, "HTTP/2 stream %" PRIu32 " reset by %s (error 0x%" PRIx32 + " %s)", ctx->tunnel.stream_id, + ctx->tunnel.reset ? "server" : "curl", + ctx->tunnel.error, nghttp2_http2_strerror(ctx->tunnel.error)); return CURLE_RECV_ERROR; } diff --git a/vendor/curl/lib/cf-haproxy.c b/vendor/curl/lib/cf-haproxy.c index 6c9bfdd..782253e 100644 --- a/vendor/curl/lib/cf-haproxy.c +++ b/vendor/curl/lib/cf-haproxy.c @@ -156,7 +156,6 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, static void cf_haproxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - (void)data; CURL_TRC_CF(data, cf, "destroy"); cf_haproxy_ctx_free(cf->ctx); } diff --git a/vendor/curl/lib/cf-https-connect.c b/vendor/curl/lib/cf-https-connect.c index d2a08d1..cabc6ab 100644 --- a/vendor/curl/lib/cf-https-connect.c +++ b/vendor/curl/lib/cf-https-connect.c @@ -30,6 +30,7 @@ #include "cfilters.h" #include "connect.h" #include "hostip.h" +#include "httpsrr.h" #include "multiif.h" #include "cf-https-connect.h" #include "http2.h" @@ -221,6 +222,8 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, /* install the winning filter below this one. */ cf->next = winner->cf; winner->cf = NULL; + /* whatever errors where reported by ballers, clear our errorbuf */ + Curl_reset_fail(data); #ifdef USE_NGHTTP2 { @@ -546,7 +549,6 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; - (void)data; CURL_TRC_CF(data, cf, "destroy"); cf_hc_reset(cf, data); Curl_safefree(ctx); diff --git a/vendor/curl/lib/cf-ip-happy.c b/vendor/curl/lib/cf-ip-happy.c index 05e9505..dfbf4ae 100644 --- a/vendor/curl/lib/cf-ip-happy.c +++ b/vendor/curl/lib/cf-ip-happy.c @@ -50,6 +50,7 @@ #include "connect.h" #include "cfilters.h" #include "cf-ip-happy.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "multiif.h" #include "progress.h" @@ -227,7 +228,7 @@ static CURLcode cf_ip_attempt_connect(struct cf_ip_attempt *a, struct Curl_easy *data, bool *connected) { - *connected = a->connected; + *connected = (bool)a->connected; if(!a->result && !*connected) { /* evaluate again */ a->result = Curl_conn_cf_connect(a->cf, data, connected); @@ -350,7 +351,8 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, struct cf_ip_attempt *a = NULL, **panchor; bool do_more; timediff_t next_expire_ms; - int i, inconclusive, ongoing; + int inconclusive, ongoing; + VERBOSE(int i); if(bs->winner) return CURLE_OK; @@ -359,9 +361,9 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, ongoing = inconclusive = 0; /* check if a running baller connects now */ - i = -1; + VERBOSE(i = -1); for(panchor = &bs->running; *panchor; panchor = &((*panchor)->next)) { - ++i; + VERBOSE(++i); a = *panchor; a->result = cf_ip_attempt_connect(a, data, connected); if(!a->result) { @@ -425,6 +427,10 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, addr = cf_ai_iter_next(&bs->addr_iter); ai_family = bs->addr_iter.ai_family; } + /* We are (re-)starting attempts. We are not interested in + * keeping old failure information. The new attempt will either + * succeed or persist new failure. */ + Curl_reset_fail(data); if(addr) { /* try another address */ result = cf_ip_attempt_new(&a, cf, data, addr, ai_family, @@ -454,9 +460,9 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, timediff_t delay_ms = bs->attempt_delay_ms - since_ms; if(delay_ms <= 0) { CURL_TRC_CF(data, cf, "all attempts inconclusive, restarting one"); - i = -1; + VERBOSE(i = -1); for(a = bs->running; a; a = a->next) { - ++i; + VERBOSE(++i); if(!a->inconclusive) continue; result = cf_ip_attempt_restart(a, cf, data); @@ -481,7 +487,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, /* no more addresses, no inconclusive attempts */ CURL_TRC_CF(data, cf, "no more attempts to try"); result = CURLE_COULDNT_CONNECT; - i = 0; + VERBOSE(i = 0); for(a = bs->running; a; a = a->next) { CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result); if(a->result) @@ -495,11 +501,11 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, bool more_possible; /* when do we need to be called again? */ - next_expire_ms = Curl_timeleft_ms(data, TRUE); - if(next_expire_ms <= 0) { + next_expire_ms = Curl_timeleft_ms(data); + if(next_expire_ms < 0) { failf(data, "Connection timeout after %" FMT_OFF_T " ms", - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle)); + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle)); return CURLE_OPERATION_TIMEDOUT; } @@ -698,7 +704,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf, if(!dns) return CURLE_FAILED_INIT; - if(Curl_timeleft_ms(data, TRUE) < 0) { + if(Curl_timeleft_ms(data) < 0) { /* a precaution, no need to continue if time already is up */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; @@ -791,10 +797,12 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, ctx->ballers.winner->cf = NULL; cf_ip_happy_ctx_clear(cf, data); Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); + /* whatever errors where reported by ballers, clear our errorbuf */ + Curl_reset_fail(data); - if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) + if(cf->conn->scheme->protocol & PROTO_FAMILY_SSH) Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_cf_is_verbose(cf, data)) { struct ip_quadruple ipquad; bool is_ipv6; diff --git a/vendor/curl/lib/cf-socket.c b/vendor/curl/lib/cf-socket.c index ce728f5..2ebbe4d 100644 --- a/vendor/curl/lib/cf-socket.c +++ b/vendor/curl/lib/cf-socket.c @@ -55,12 +55,12 @@ #endif #include "urldata.h" -#include "bufq.h" #include "curl_trc.h" #include "if2ip.h" #include "cfilters.h" #include "cf-socket.h" #include "connect.h" +#include "curl_addrinfo.h" #include "select.h" #include "multiif.h" #include "curlx/inet_pton.h" @@ -68,7 +68,7 @@ #include "conncache.h" #include "multihandle.h" #include "rand.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "system_win32.h" #include "curlx/nonblock.h" #include "curlx/version_win32.h" @@ -83,7 +83,7 @@ static void tcpnodelay(struct Curl_cfilter *cf, #if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED) curl_socklen_t onoff = (curl_socklen_t)1; int level = IPPROTO_TCP; - char buffer[STRERROR_LEN]; + VERBOSE(char buffer[STRERROR_LEN]); if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) @@ -96,42 +96,20 @@ static void tcpnodelay(struct Curl_cfilter *cf, #endif } -#ifdef SO_NOSIGPIPE -/* The preferred method on macOS (10.2 and later) to prevent SIGPIPEs when - sending data to a dead peer (instead of relying on the 4th argument to send - being MSG_NOSIGNAL). Possibly also existing and in use on other BSD - systems? */ -static void nosigpipe(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t sockfd) -{ - int onoff = 1; - if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, - (void *)&onoff, sizeof(onoff)) < 0) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; - CURL_TRC_CF(data, cf, "Could not set SO_NOSIGPIPE: %s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#else - (void)cf; - (void)data; -#endif - } -} -#else -#define nosigpipe(x, y, z) Curl_nop_stmt -#endif - +#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \ + defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \ + defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD) #if defined(USE_WINSOCK) || \ (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ (defined(__DragonFly__) && __DragonFly_version < 500702) || \ (defined(_WIN32) && !defined(TCP_KEEPIDLE)) /* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299 * use millisecond units. */ -#define KEEPALIVE_FACTOR(x) (x *= 1000) +#define KEEPALIVE_FACTOR(x) ((x) *= 1000) #else #define KEEPALIVE_FACTOR(x) #endif +#endif static void tcpkeepalive(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -288,11 +266,11 @@ static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest, uint8_t transport) { /* - * The Curl_sockaddr_ex structure is basically libcurl's external API - * curl_sockaddr structure with enough space available to directly hold - * any protocol-specific address structures. The variable declared here - * will be used to pass / receive data to/from the fopensocket callback - * if this has been set, before that, it is initialized from parameters. + * The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr + * structure with enough space available to directly hold any + * protocol-specific address structures. The variable declared here will be + * used to pass / receive data to/from the fopensocket callback if this has + * been set, before that, it is initialized from parameters. */ dest->family = ai->ai_family; switch(transport) { @@ -319,12 +297,25 @@ static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest, return CURLE_OK; } +#ifdef USE_SO_NOSIGPIPE +int Curl_sock_nosigpipe(curl_socket_t sockfd) +{ + int onoff = 1; + return setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, + (void *)&onoff, sizeof(onoff)); +} +#endif /* USE_SO_NOSIGPIPE */ + static CURLcode socket_open(struct Curl_easy *data, struct Curl_sockaddr_ex *addr, curl_socket_t *sockfd) { char errbuf[STRERROR_LEN]; +#ifdef SOCK_CLOEXEC + addr->socktype |= SOCK_CLOEXEC; +#endif + DEBUGASSERT(data); DEBUGASSERT(data->conn); if(data->set.fopensocket) { @@ -344,7 +335,13 @@ static CURLcode socket_open(struct Curl_easy *data, Curl_set_in_callback(data, FALSE); } else { - /* opensocket callback not set, so simply create the socket now */ + /* opensocket callback not set, so create the socket now */ +#ifdef DEBUGBUILD + if((addr->family == AF_INET6) && getenv("CURL_DBG_SOCK_FAIL_IPV6")) { + failf(data, "CURL_DBG_SOCK_FAIL_IPV6: failed to open socket"); + return CURLE_COULDNT_CONNECT; + } +#endif *sockfd = CURL_SOCKET(addr->family, addr->socktype, addr->protocol); if((*sockfd == CURL_SOCKET_BAD) && (SOCKERRNO == SOCKENOMEM)) return CURLE_OUT_OF_MEMORY; @@ -357,11 +354,21 @@ static CURLcode socket_open(struct Curl_easy *data, return CURLE_COULDNT_CONNECT; } -#ifdef HAVE_FCNTL +#ifdef USE_SO_NOSIGPIPE + if(Curl_sock_nosigpipe(*sockfd) < 0) { + failf(data, "setsockopt enable SO_NOSIGPIPE: %s", + curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); + sclose(*sockfd); + *sockfd = CURL_SOCKET_BAD; + return CURLE_COULDNT_CONNECT; + } +#endif /* USE_SO_NOSIGPIPE */ + +#if defined(HAVE_FCNTL) && !defined(SOCK_CLOEXEC) if(fcntl(*sockfd, F_SETFD, FD_CLOEXEC) < 0) { failf(data, "fcntl set CLOEXEC: %s", curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); - close(*sockfd); + sclose(*sockfd); *sockfd = CURL_SOCKET_BAD; return CURLE_COULDNT_CONNECT; } @@ -408,7 +415,7 @@ CURLcode Curl_socket_open(struct Curl_easy *data, static int socket_close(struct Curl_easy *data, struct connectdata *conn, int use_callback, curl_socket_t sock) { - if(CURL_SOCKET_BAD == sock) + if(sock == CURL_SOCKET_BAD) return 0; if(use_callback && conn && conn->fclosesocket) { @@ -440,37 +447,6 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, return socket_close(data, conn, FALSE, sock); } -#ifdef USE_WINSOCK -/* When you run a program that uses the Windows Sockets API, you may - experience slow performance when you copy data to a TCP server. - - https://learn.microsoft.com/troubleshoot/windows-server/networking/slow-performance-copy-data-tcp-server-sockets-api - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - - The problem described in this knowledge-base is applied only to pre-Vista - Windows. Following function trying to detect OS version and skips - SO_SNDBUF adjustment for Windows Vista and above. -*/ - -void Curl_sndbuf_init(curl_socket_t sockfd) -{ - int val = CURL_MAX_WRITE_SIZE + 32; - int curval = 0; - int curlen = sizeof(curval); - - if(Curl_isVistaOrGreater) - return; - - if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) - if(curval > val) - return; - - setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); -} -#endif /* USE_WINSOCK */ - /* * Curl_parse_interface() * @@ -516,14 +492,14 @@ CURLcode Curl_parse_interface(const char *input, input += strlen(if_prefix); if(!*input) return CURLE_BAD_FUNCTION_ARGUMENT; - *iface = Curl_memdup0(input, len - strlen(if_prefix)); + *iface = curlx_memdup0(input, len - strlen(if_prefix)); return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY; } else if(!strncmp(host_prefix, input, strlen(host_prefix))) { input += strlen(host_prefix); if(!*input) return CURLE_BAD_FUNCTION_ARGUMENT; - *host = Curl_memdup0(input, len - strlen(host_prefix)); + *host = curlx_memdup0(input, len - strlen(host_prefix)); return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY; } else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) { @@ -533,11 +509,11 @@ CURLcode Curl_parse_interface(const char *input, host_part = memchr(input, '!', len); if(!host_part || !*(host_part + 1)) return CURLE_BAD_FUNCTION_ARGUMENT; - *iface = Curl_memdup0(input, host_part - input); + *iface = curlx_memdup0(input, host_part - input); if(!*iface) return CURLE_OUT_OF_MEMORY; ++host_part; - *host = Curl_memdup0(host_part, len - (host_part - input)); + *host = curlx_memdup0(host_part, len - (host_part - input)); if(!*host) { curlx_free(*iface); *iface = NULL; @@ -548,7 +524,7 @@ CURLcode Curl_parse_interface(const char *input, if(!*input) return CURLE_BAD_FUNCTION_ARGUMENT; - *dev = Curl_memdup0(input, len); + *dev = curlx_memdup0(input, len); return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY; } @@ -604,10 +580,10 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, * This binds the local socket to a particular interface. This will * force even requests to other local interfaces to go out the external * interface. Only bind to the interface when specified as interface, - * not just as a hostname or ip address. + * not as a hostname or ip address. * * The interface might be a VRF, eg: vrf-blue, which means it cannot be - * converted to an IP address and would fail Curl_if2ip. Simply try to + * converted to an IP address and would fail Curl_if2ip. Try to * use it straight away. */ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, @@ -822,7 +798,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) * * "I do not have Rational Quantify, but the hint from his post was * ntdll::NtRemoveIoCompletion(). I would assume the SleepEx (or maybe - * just Sleep(0) would be enough?) would release whatever + * Sleep(0) would be enough?) would release whatever * mutex/critical-section the ntdll call is waiting on. * * Someone got to verify this on Win-NT 4.0, 2000." @@ -877,15 +853,12 @@ static CURLcode socket_connect_result(struct Curl_easy *data, default: /* unknown error, fallthrough and try another address! */ -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)ipaddress; -#else { - char buffer[STRERROR_LEN]; + VERBOSE(char buffer[STRERROR_LEN]); infof(data, "Immediate connect fail for %s: %s", ipaddress, curlx_strerror(error, buffer, sizeof(buffer))); + NOVERBOSE((void)ipaddress); } -#endif data->state.os_errno = error; /* connect failed */ return CURLE_COULDNT_CONNECT; @@ -968,7 +941,7 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; - if(ctx && CURL_SOCKET_BAD != ctx->sock) { + if(ctx && ctx->sock != CURL_SOCKET_BAD) { CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock); if(ctx->sock == cf->conn->sock[cf->sockindex]) cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; @@ -1023,16 +996,15 @@ static void set_local_ip(struct Curl_cfilter *cf, #ifdef HAVE_GETSOCKNAME if((ctx->sock != CURL_SOCKET_BAD) && - !(data->conn->handler->protocol & CURLPROTO_TFTP)) { + !(data->conn->scheme->protocol & CURLPROTO_TFTP)) { /* TFTP does not connect, so it cannot get the IP like this */ - - char buffer[STRERROR_LEN]; struct Curl_sockaddr_storage ssloc; curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); + VERBOSE(char buffer[STRERROR_LEN]); memset(&ssloc, 0, sizeof(ssloc)); if(getsockname(ctx->sock, (struct sockaddr *)&ssloc, &slen)) { - int error = SOCKERRNO; + VERBOSE(int error = SOCKERRNO); infof(data, "getsockname() failed with errno %d: %s", error, curlx_strerror(error, buffer, sizeof(buffer))); } @@ -1068,6 +1040,19 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, return CURLE_OK; } +/* to figure out the type of the socket safely, remove the possibly ORed + bits before comparing */ +static int cf_socktype(int x) +{ +#ifdef SOCK_CLOEXEC + x &= ~SOCK_CLOEXEC; +#endif +#ifdef SOCK_NONBLOCK + x &= ~SOCK_NONBLOCK; +#endif + return x; +} + static CURLcode cf_socket_open(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1077,7 +1062,6 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, CURLcode result = CURLE_COULDNT_CONNECT; bool is_tcp; - (void)data; DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); ctx->started_at = *Curl_pgrs_now(data); #ifdef SOCK_NONBLOCK @@ -1124,18 +1108,14 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, #ifdef USE_IPV6 is_tcp = (ctx->addr.family == AF_INET || ctx->addr.family == AF_INET6) && - ctx->addr.socktype == SOCK_STREAM; + cf_socktype(ctx->addr.socktype) == SOCK_STREAM; #else is_tcp = (ctx->addr.family == AF_INET) && - ctx->addr.socktype == SOCK_STREAM; + cf_socktype(ctx->addr.socktype) == SOCK_STREAM; #endif if(is_tcp && data->set.tcp_nodelay) tcpnodelay(cf, data, ctx->sock); - nosigpipe(cf, data, ctx->sock); - - Curl_sndbuf_init(ctx->sock); - if(is_tcp && data->set.tcp_keepalive) tcpkeepalive(cf, data, ctx->sock); @@ -1196,7 +1176,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, } } #endif - ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM); + ctx->sock_connected = (cf_socktype(ctx->addr.socktype) != SOCK_DGRAM); out: if(result) { if(ctx->sock != CURL_SOCKET_BAD) { @@ -1277,7 +1257,6 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, CURLcode result = CURLE_COULDNT_CONNECT; int rc = 0; - (void)data; if(cf->connected) { *done = TRUE; return CURLE_OK; @@ -1297,12 +1276,12 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, } /* Connect TCP socket */ - rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); + rc = do_connect(cf, data, (bool)cf->conn->bits.tcp_fastopen); error = SOCKERRNO; set_local_ip(cf, data); CURL_TRC_CF(data, cf, "local address %s port %d...", ctx->ip.local_ip, ctx->ip.local_port); - if(-1 == rc) { + if(rc == -1) { result = socket_connect_result(data, ctx->ip.remote_ip, error); goto out; } @@ -1341,18 +1320,14 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, out: if(result) { if(ctx->error) { + VERBOSE(char buffer[STRERROR_LEN]); set_local_ip(cf, data); data->state.os_errno = ctx->error; SET_SOCKERRNO(ctx->error); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - { - char buffer[STRERROR_LEN]; - infof(data, "connect to %s port %u from %s port %d failed: %s", - ctx->ip.remote_ip, ctx->ip.remote_port, - ctx->ip.local_ip, ctx->ip.local_port, - curlx_strerror(ctx->error, buffer, sizeof(buffer))); - } -#endif + infof(data, "connect to %s port %u from %s port %d failed: %s", + ctx->ip.remote_ip, ctx->ip.remote_port, + ctx->ip.local_ip, ctx->ip.local_port, + curlx_strerror(ctx->error, buffer, sizeof(buffer))); } if(ctx->sock != CURL_SOCKET_BAD) { socket_close(data, cf->conn, TRUE, ctx->sock); @@ -1429,8 +1404,8 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_socket_ctx *ctx = cf->ctx; curl_socket_t fdsave; ssize_t rv; - size_t orig_len = len; CURLcode result = CURLE_OK; + VERBOSE(size_t orig_len = len); (void)eos; *pnwritten = 0; @@ -1483,7 +1458,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, (SOCKEINPROGRESS == sockerr) #endif ) { - /* this is just a case of EWOULDBLOCK */ + /* EWOULDBLOCK */ result = CURLE_AGAIN; } else { @@ -1525,10 +1500,9 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } } if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) { - size_t orig_len = len; - len = ctx->recv_max; CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes", - orig_len, len); + len, ctx->recv_max); + len = ctx->recv_max; } #endif @@ -1549,7 +1523,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, (EAGAIN == sockerr) || (SOCKEINTR == sockerr) #endif ) { - /* this is just a case of EWOULDBLOCK */ + /* EWOULDBLOCK */ result = CURLE_AGAIN; } else { @@ -1627,7 +1601,7 @@ static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, int r; *input_pending = FALSE; - (void)data; + if(!ctx || ctx->sock == CURL_SOCKET_BAD) return FALSE; @@ -1827,7 +1801,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, NOLINTNEXTLINE(clang-analyzer-unix.StdCLibraryFunctions) */ rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, (curl_socklen_t)ctx->addr.addrlen); - if(-1 == rc) { + if(rc == -1) { return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO); } ctx->sock_connected = TRUE; @@ -2004,7 +1978,7 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf, #endif /* check if the generic timeout possibly is set shorter */ - other_ms = Curl_timeleft_ms(data, FALSE); + other_ms = Curl_timeleft_ms(data); if(other_ms && (other_ms < timeout_ms)) /* note that this also works fine for when other_ms happens to be negative due to it already having elapsed */ @@ -2113,7 +2087,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *)&add, &size); #endif - if(CURL_SOCKET_BAD == s_accepted) { + if(s_accepted == CURL_SOCKET_BAD) { failf(data, "Error accept()ing server connect: %s", curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); return CURLE_FTP_ACCEPT_FAILED; diff --git a/vendor/curl/lib/cf-socket.h b/vendor/curl/lib/cf-socket.h index 9db492e..014f5f7 100644 --- a/vendor/curl/lib/cf-socket.h +++ b/vendor/curl/lib/cf-socket.h @@ -35,8 +35,8 @@ struct Curl_sockaddr_ex; struct ip_quadruple; /* - * The Curl_sockaddr_ex structure is basically libcurl's external API - * curl_sockaddr structure with enough space available to directly hold any + * The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr + * structure with enough space available to directly hold any * protocol-specific address structures. The variable declared here will be * used to pass / receive data to/from the fopensocket callback if this has * been set, before that, it is initialized from parameters. @@ -73,24 +73,14 @@ CURLcode Curl_socket_open(struct Curl_easy *data, uint8_t transport, curl_socket_t *sockfd); +#ifdef USE_SO_NOSIGPIPE +/* Set SO_NOSIGPIPE on socket, return < 0 on error. */ +int Curl_sock_nosigpipe(curl_socket_t sockfd); +#endif + int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sock); -#ifdef USE_WINSOCK -/* When you run a program that uses the Windows Sockets API, you may - experience slow performance when you copy data to a TCP server. - - https://learn.microsoft.com/troubleshoot/windows-server/networking/slow-performance-copy-data-tcp-server-sockets-api - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - -*/ -void Curl_sndbuf_init(curl_socket_t sockfd); -#else -#define Curl_sndbuf_init(y) Curl_nop_stmt -#endif - /** * Creates a cfilter that opens a TCP socket to the given address * when calling its `connect` implementation. diff --git a/vendor/curl/lib/cfilters.c b/vendor/curl/lib/cfilters.c index b91d30c..27b0306 100644 --- a/vendor/curl/lib/cfilters.c +++ b/vendor/curl/lib/cfilters.c @@ -33,9 +33,6 @@ #include "select.h" #include "curlx/strparse.h" -static void cf_cntrl_update_info(struct Curl_easy *data, - struct connectdata *conn); - #ifdef UNITTESTS /* used by unit2600.c */ void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -55,9 +52,6 @@ CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, return CURLE_OK; } -static void conn_report_connect_stats(struct Curl_cfilter *cf, - struct Curl_easy *data); - CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -142,22 +136,22 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, } void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, int index) + struct connectdata *conn, int sockindex) { - Curl_conn_cf_discard_chain(&conn->cfilter[index], data); + Curl_conn_cf_discard_chain(&conn->cfilter[sockindex], data); } -void Curl_conn_close(struct Curl_easy *data, int index) +void Curl_conn_close(struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf; DEBUGASSERT(data->conn); /* it is valid to call that without filters being present */ - cf = data->conn->cfilter[index]; + cf = data->conn->cfilter[sockindex]; if(cf) { cf->cft->do_close(cf, data); } - Curl_shutdown_clear(data, index); + Curl_shutdown_clear(data, sockindex); } CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) @@ -183,8 +177,6 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) *done = FALSE; if(!Curl_shutdown_started(data, sockindex)) { - CURL_TRC_M(data, "shutdown start on%s connection", - sockindex ? " secondary" : ""); Curl_shutdown_start(data, sockindex, 0); } else { @@ -217,14 +209,14 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) return result; } -CURLcode Curl_cf_recv(struct Curl_easy *data, int num, char *buf, +CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, size_t len, size_t *pnread) { struct Curl_cfilter *cf; DEBUGASSERT(data); DEBUGASSERT(data->conn); - cf = data->conn->cfilter[num]; + cf = data->conn->cfilter[sockindex]; while(cf && !cf->connected) cf = cf->next; if(cf) @@ -235,19 +227,19 @@ CURLcode Curl_cf_recv(struct Curl_easy *data, int num, char *buf, return CURLE_FAILED_INIT; } -CURLcode Curl_cf_send(struct Curl_easy *data, int num, - const uint8_t *mem, size_t len, bool eos, +CURLcode Curl_cf_send(struct Curl_easy *data, int sockindex, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct Curl_cfilter *cf; DEBUGASSERT(data); DEBUGASSERT(data->conn); - cf = data->conn->cfilter[num]; + cf = data->conn->cfilter[sockindex]; while(cf && !cf->connected) cf = cf->next; if(cf) { - return cf->cft->do_send(cf, data, mem, len, eos, pnwritten); + return cf->cft->do_send(cf, data, buf, len, eos, pnwritten); } failf(data, "send: no filter connected"); DEBUGASSERT(0); @@ -336,18 +328,17 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf, void Curl_conn_cf_add(struct Curl_easy *data, struct connectdata *conn, - int index, + int sockindex, struct Curl_cfilter *cf) { - (void)data; DEBUGASSERT(conn); DEBUGASSERT(!cf->conn); DEBUGASSERT(!cf->next); - cf->next = conn->cfilter[index]; + cf->next = conn->cfilter[sockindex]; cf->conn = conn; - cf->sockindex = index; - conn->cfilter[index] = cf; + cf->sockindex = sockindex; + conn->cfilter[sockindex] = cf; CURL_TRC_CF(data, cf, "added"); } @@ -429,7 +420,7 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return CURLE_RECV_ERROR; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static CURLcode cf_verboseconnect(struct Curl_easy *data, struct Curl_cfilter *cf) { @@ -452,6 +443,51 @@ static CURLcode cf_verboseconnect(struct Curl_easy *data, } #endif +static CURLcode cf_cntrl_all(struct connectdata *conn, + struct Curl_easy *data, + bool ignore_result, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + size_t i; + + for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) { + result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result, + event, arg1, arg2); + if(!ignore_result && result) + break; + } + return result; +} + +static void cf_cntrl_update_info(struct Curl_easy *data, + struct connectdata *conn) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); +} + +/** + * Update connection statistics + */ +static void conn_report_connect_stats(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + 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); + } +} + CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, bool blocking, @@ -475,7 +511,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, return CURLE_FAILED_INIT; } - *done = cf->connected; + *done = (bool)cf->connected; if(*done) return CURLE_OK; @@ -499,9 +535,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, cf_cntrl_update_info(data, data->conn); conn_report_connect_stats(cf, data); data->conn->keepalive = *Curl_pgrs_now(data); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - result = cf_verboseconnect(data, cf); -#endif + VERBOSE(result = cf_verboseconnect(data, cf)); goto out; } else if(result) { @@ -514,7 +548,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, goto out; else { /* check allowed time left */ - const timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE); + const timediff_t timeout_ms = Curl_timeleft_ms(data); curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); int rc; @@ -572,8 +606,8 @@ bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) return FALSE; cf = conn->cfilter[sockindex]; if(cf) - return cf->connected; - else if(conn->handler->flags & PROTOPT_NONETWORK) + return (bool)cf->connected; + else if(conn->scheme->flags & PROTOPT_NONETWORK) return TRUE; return FALSE; } @@ -837,7 +871,7 @@ CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; for(; cf; cf = cf->next) { - if(Curl_cf_def_cntrl == cf->cft->cntrl) + if(cf->cft->cntrl == Curl_cf_def_cntrl) continue; result = cf->cft->cntrl(cf, data, event, arg1, arg2); if(!ignore_result && result) @@ -923,23 +957,6 @@ Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex) return cf ? cf_get_remote_addr(cf, data) : NULL; } -static CURLcode cf_cntrl_all(struct connectdata *conn, - struct Curl_easy *data, - bool ignore_result, - int event, int arg1, void *arg2) -{ - CURLcode result = CURLE_OK; - size_t i; - - for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) { - result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result, - event, arg1, arg2); - if(!ignore_result && result) - break; - } - return result; -} - CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data) { return cf_cntrl_all(data->conn, data, FALSE, CF_CTRL_DATA_SETUP, 0, NULL); @@ -977,34 +994,6 @@ CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause) CF_CTRL_DATA_PAUSE, do_pause, NULL); } -static void cf_cntrl_update_info(struct Curl_easy *data, - struct connectdata *conn) -{ - cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); -} - -/** - * Update connection statistics - */ -static void conn_report_connect_stats(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - 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 *input_pending) { @@ -1071,23 +1060,23 @@ int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd) } CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, - char *buf, size_t blen, size_t *pnread) + char *buf, size_t len, size_t *pnread) { DEBUGASSERT(data); DEBUGASSERT(data->conn); if(!CONN_SOCK_IDX_VALID(sockindex)) return CURLE_BAD_FUNCTION_ARGUMENT; if(data && data->conn && data->conn->recv[sockindex]) - return data->conn->recv[sockindex](data, sockindex, buf, blen, pnread); + return data->conn->recv[sockindex](data, sockindex, buf, len, pnread); *pnread = 0; return CURLE_FAILED_INIT; } CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t blen, bool eos, + const void *buf, size_t len, bool eos, size_t *pnwritten) { - size_t write_len = blen; + size_t write_len = len; DEBUGASSERT(data); DEBUGASSERT(data->conn); @@ -1100,13 +1089,14 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, const char *p = getenv("CURL_SMALLSENDS"); if(p) { curl_off_t altsize; - if(!curlx_str_number(&p, &altsize, write_len)) + if(!curlx_str_number(&p, &altsize, write_len)) { write_len = (size_t)altsize; + if(write_len != len) + eos = FALSE; + } } } #endif - if(write_len != blen) - eos = FALSE; if(data && data->conn && data->conn->send[sockindex]) return data->conn->send[sockindex](data, sockindex, buf, write_len, eos, pnwritten); diff --git a/vendor/curl/lib/cfilters.h b/vendor/curl/lib/cfilters.h index 9faf01d..c754634 100644 --- a/vendor/curl/lib/cfilters.h +++ b/vendor/curl/lib/cfilters.h @@ -117,7 +117,7 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, */ /* data event arg1 arg2 return */ #define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */ -/* unused now 5 */ +/* unused now 5 */ #define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */ #define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */ #define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */ @@ -170,7 +170,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, #define CF_QUERY_STREAM_ERROR 6 /* error code - */ #define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */ #define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */ -#define CF_QUERY_HTTP_VERSION 9 /* number (10/11/20/30) - */ +#define CF_QUERY_HTTP_VERSION 9 /* number (10/11/20/30) - */ /* pass in a `const struct Curl_sockaddr_ex **` as `pres2`. Gets set * to NULL when not connected. */ #define CF_QUERY_REMOTE_ADDR 10 /* - `Curl_sockaddr_ex *` */ @@ -312,8 +312,7 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, * Remove and destroy all filters at chain `sockindex` on connection `conn`. */ void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); + struct connectdata *conn, int sockindex); CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -349,7 +348,7 @@ unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data); -#define CURL_CF_SSL_DEFAULT -1 +#define CURL_CF_SSL_DEFAULT (-1) #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 @@ -608,15 +607,14 @@ int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd); * Will return CURLE_AGAIN iff blocked on receiving. */ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, - char *buf, size_t buffersize, - size_t *pnread); + char *buf, size_t len, size_t *pnread); /* * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. * Will return CURLE_AGAIN iff blocked on sending. */ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t blen, bool eos, + const void *buf, size_t len, bool eos, size_t *pnwritten); /** diff --git a/vendor/curl/lib/config-mac.h b/vendor/curl/lib/config-mac.h index 6da544f..e9806b4 100644 --- a/vendor/curl/lib/config-mac.h +++ b/vendor/curl/lib/config-mac.h @@ -35,9 +35,6 @@ #endif #include -#if TYPE_LONGLONG -#define HAVE_LONGLONG 1 -#endif /* Define if you want the built-in manual */ #define USE_MANUAL 1 @@ -62,18 +59,14 @@ #define HAVE_SIGACTION 1 -#define CURL_DISABLE_LDAP 1 +#define CURL_DISABLE_LDAP #define HAVE_IOCTL_FIONBIO 1 #define SIZEOF_INT 4 #define SIZEOF_LONG 4 #define SIZEOF_SIZE_T 4 -#ifdef HAVE_LONGLONG #define SIZEOF_CURL_OFF_T 8 -#else -#define SIZEOF_CURL_OFF_T 4 -#endif #define HAVE_RECV 1 #define RECV_TYPE_ARG1 int @@ -84,7 +77,6 @@ #define HAVE_SEND 1 #define SEND_TYPE_ARG1 int -#define SEND_QUAL_ARG2 const #define SEND_TYPE_ARG2 void * #define SEND_TYPE_ARG3 size_t #define SEND_TYPE_ARG4 int diff --git a/vendor/curl/lib/config-os400.h b/vendor/curl/lib/config-os400.h index ec6c002..504fded 100644 --- a/vendor/curl/lib/config-os400.h +++ b/vendor/curl/lib/config-os400.h @@ -37,7 +37,7 @@ /* OS400 supports a 3-argument ASCII version of gethostbyaddr_r(), but its * prototype is incompatible with the "standard" one (1st argument is not - * const). However, getaddrinfo() is supported (ASCII version defined as + * const). getaddrinfo() is supported (ASCII version defined as * a local wrapper in setup-os400.h) in a thread-safe way: we can then * configure getaddrinfo() as such and get rid of gethostbyname_r() without * loss of thread-safeness. */ @@ -148,9 +148,6 @@ /* Define if you have the `stricmp' function. */ #define HAVE_STRICMP -/* Define if you have the `strdup' function. */ -#define HAVE_STRDUP - /* Define if you have the header file. */ #define HAVE_STRINGS_H @@ -187,12 +184,6 @@ /* The size of `int', as computed by sizeof. */ #define SIZEOF_INT 4 -/* Define if the compiler supports the 'long long' data type. */ -#define HAVE_LONGLONG - -/* The size of a `long long', as computed by sizeof. */ -#define SIZEOF_LONG_LONG 8 - /* The size of `long', as computed by sizeof. */ #define SIZEOF_LONG 4 @@ -262,8 +253,8 @@ /* Define to the type of arg 1 for send. */ #define SEND_TYPE_ARG1 int -/* Define to the type qualifier of arg 2 for send. */ -#define SEND_QUAL_ARG2 +/* Define if the type qualifier of arg 2 for send is not const. */ +#define SEND_NONCONST_ARG2 /* Define to the type of arg 2 for send. */ #define SEND_TYPE_ARG2 char * diff --git a/vendor/curl/lib/config-plan9.h b/vendor/curl/lib/config-plan9.h deleted file mode 100644 index 616b8e2..0000000 --- a/vendor/curl/lib/config-plan9.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef HEADER_CURL_CONFIG_PLAN9_H -#define HEADER_CURL_CONFIG_PLAN9_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#define BUILDING_LIBCURL 1 -#define CURL_CA_BUNDLE "/sys/lib/tls/ca.pem" -#define CURL_CA_PATH "/sys/lib/tls" -#define CURL_STATICLIB 1 -#define USE_IPV6 1 -#define CURL_DISABLE_LDAP 1 - -#define NEED_REENTRANT 1 -#ifndef CURL_OS -#define CURL_OS "plan9" -#endif - -#define STDC_HEADERS 1 - -#ifdef _BITS64 -#error not implement -#else -#define SIZEOF_INT 4 -#define SIZEOF_LONG 4 -#define SIZEOF_OFF_T 8 -#define SIZEOF_CURL_OFF_T 4 /* curl_off_t = timediff_t = int */ -#define SIZEOF_SIZE_T 4 -#define SIZEOF_TIME_T 4 -#endif - -#define HAVE_RECV 1 -#define RECV_TYPE_ARG1 int -#define RECV_TYPE_ARG2 void * -#define RECV_TYPE_ARG3 int -#define RECV_TYPE_ARG4 int -#define RECV_TYPE_RETV int - -#define HAVE_SELECT 1 - -#define HAVE_SEND 1 -#define SEND_TYPE_ARG1 int -#define SEND_TYPE_ARG2 void * -#define SEND_QUAL_ARG2 -#define SEND_TYPE_ARG3 int -#define SEND_TYPE_ARG4 int -#define SEND_TYPE_RETV int - -#define HAVE_ALARM 1 -#define HAVE_ARPA_INET_H 1 -#define HAVE_BASENAME 1 -#define HAVE_BOOL_T 1 -#define HAVE_FCNTL 1 -#define HAVE_FCNTL_H 1 -#define HAVE_FREEADDRINFO 1 -#define HAVE_FTRUNCATE 1 -#define HAVE_GETADDRINFO 1 -#define HAVE_GETEUID 1 -#define HAVE_GETHOSTNAME 1 -#define HAVE_GETPPID 1 -#define HAVE_GETPWUID 1 -#define HAVE_GETTIMEOFDAY 1 -#define HAVE_GMTIME_R 1 -#define HAVE_INET_NTOP 1 -#define HAVE_INET_PTON 1 -#define HAVE_LIBGEN_H 1 -#define HAVE_LIBZ 1 -#define HAVE_LOCALE_H 1 -#define HAVE_LOCALTIME_R 1 -#define HAVE_LONGLONG 1 -#define HAVE_NETDB_H 1 -#define HAVE_NETINET_IN_H 1 -#define HAVE_NETINET_TCP_H 1 -#define HAVE_PWD_H 1 -#define HAVE_SYS_SELECT_H 1 - -#define USE_OPENSSL 1 - -#define HAVE_PIPE 1 -#define HAVE_POLL 1 -#define HAVE_POLL_H 1 -#define HAVE_PTHREAD_H 1 -#define HAVE_SETLOCALE 1 - -#define HAVE_SIGACTION 1 -#define HAVE_SIGNAL 1 -#define HAVE_SIGSETJMP 1 -#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 -#define HAVE_SOCKET 1 -#define HAVE_STDBOOL_H 1 -#define HAVE_STRCASECMP 1 -#define HAVE_STRDUP 1 -#define HAVE_STRUCT_TIMEVAL 1 -#define HAVE_SYS_IOCTL_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_RESOURCE_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_SYS_UN_H 1 -#define HAVE_TERMIOS_H 1 -#define HAVE_UNISTD_H 1 -#define HAVE_UTIME 1 -#define HAVE_UTIME_H 1 - -#define HAVE_POSIX_STRERROR_R 1 -#define HAVE_STRERROR_R 1 -#define USE_MANUAL 1 - -#define __attribute__(x) - -#ifndef __cplusplus -#undef inline -#endif - -#endif /* HEADER_CURL_CONFIG_PLAN9_H */ diff --git a/vendor/curl/lib/config-riscos.h b/vendor/curl/lib/config-riscos.h index 3158c98..879be03 100644 --- a/vendor/curl/lib/config-riscos.h +++ b/vendor/curl/lib/config-riscos.h @@ -36,36 +36,15 @@ /* Define if you want the built-in manual */ #define USE_MANUAL -/* Define if you have the gethostbyname_r() function with 3 arguments */ -#undef HAVE_GETHOSTBYNAME_R_3 - -/* Define if you have the gethostbyname_r() function with 5 arguments */ -#undef HAVE_GETHOSTBYNAME_R_5 - -/* Define if you have the gethostbyname_r() function with 6 arguments */ -#undef HAVE_GETHOSTBYNAME_R_6 - -/* Define if you need the _REENTRANT define for some functions */ -#undef NEED_REENTRANT - -/* Define if you want to enable IPv6 support */ -#undef USE_IPV6 - /* Define if struct sockaddr_in6 has the sin6_scope_id member */ #define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 -/* Define this to 'int' if ssize_t is not an available typedefed type */ -#undef ssize_t - /* Define if you have the alarm function. */ #define HAVE_ALARM /* Define if you have the header file. */ #define HAVE_ARPA_INET_H -/* Define if you have the `closesocket' function. */ -#undef HAVE_CLOSESOCKET - /* Define if you have the header file. */ #define HAVE_FCNTL_H @@ -75,30 +54,15 @@ /* Define if getaddrinfo exists and works */ #define HAVE_GETADDRINFO -/* Define if you have the `geteuid' function. */ -#undef HAVE_GETEUID - -/* Define if you have the `gethostbyname_r' function. */ -#undef HAVE_GETHOSTBYNAME_R - /* Define if you have the `gethostname' function. */ #define HAVE_GETHOSTNAME -/* Define if you have the `getpass_r' function. */ -#undef HAVE_GETPASS_R - -/* Define if you have the `getpwuid' function. */ -#undef HAVE_GETPWUID - /* Define if you have the `gettimeofday' function. */ #define HAVE_GETTIMEOFDAY /* Define if you have the `timeval' struct. */ #define HAVE_STRUCT_TIMEVAL -/* Define if you have the header file. */ -#undef HAVE_IO_H - /* Define if you have the header file. */ #define HAVE_NETDB_H @@ -108,84 +72,33 @@ /* Define if you have the header file. */ #define HAVE_NET_IF_H -/* Define if you have the header file. */ -#undef HAVE_PWD_H - /* Define if you have the `select' function. */ #define HAVE_SELECT -/* Define if you have the `sigaction' function. */ -#undef HAVE_SIGACTION - /* Define if you have the `signal' function. */ #define HAVE_SIGNAL /* Define if you have the `socket' function. */ #define HAVE_SOCKET -/* Define if you have the `strcasecmp' function. */ -#undef HAVE_STRCASECMP - -/* Define if you have the `strcmpi' function. */ -#undef HAVE_STRCMPI - -/* Define if you have the `strdup' function. */ -#define HAVE_STRDUP - /* Define if you have the `stricmp' function. */ #define HAVE_STRICMP -/* Define if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define if you have the header file. */ -#undef HAVE_SYS_PARAM_H - -/* Define if you have the header file. */ -#undef HAVE_SYS_SELECT_H - -/* Define if you have the header file. */ -#undef HAVE_SYS_SOCKIO_H - /* Define if you have the header file. */ #define HAVE_SYS_TYPES_H /* Define if you have the header file. */ #define HAVE_TERMIOS_H -/* Define if you have the header file. */ -#undef HAVE_TERMIO_H - /* Define if you have the header file. */ #define HAVE_UNISTD_H /* The size of `int', as computed by sizeof. */ #define SIZEOF_INT 4 -/* The size of `long long', as computed by sizeof. */ -#undef SIZEOF_LONG_LONG - /* The size of `size_t', as computed by sizeof. */ #define SIZEOF_SIZE_T 4 -/* Define if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Version number of package */ -#undef VERSION - -/* Number of bits in a file offset, on hosts where this is settable. */ -#undef _FILE_OFFSET_BITS - -/* Define to empty if `const' does not conform to ANSI C. */ -#undef const - -/* Define to `unsigned' if does not define. */ -#undef size_t - -/* Define to `int' if does not define. */ -#undef ssize_t - /* Define if you have a working ioctl FIONBIO function. */ #define HAVE_IOCTL_FIONBIO @@ -216,9 +129,6 @@ /* Define to the type of arg 1 for send. */ #define SEND_TYPE_ARG1 int -/* Define to the type qualifier of arg 2 for send. */ -#define SEND_QUAL_ARG2 const - /* Define to the type of arg 2 for send. */ #define SEND_TYPE_ARG2 void * diff --git a/vendor/curl/lib/config-win32.h b/vendor/curl/lib/config-win32.h index 002782c..ddde06a 100644 --- a/vendor/curl/lib/config-win32.h +++ b/vendor/curl/lib/config-win32.h @@ -28,53 +28,6 @@ /* Hand crafted config file for Windows */ /* ================================================================ */ -/* Define some minimum and default build targets for Visual Studio */ -#ifdef _MSC_VER - /* VS2012 default target settings and minimum build target check. */ -# if _MSC_VER >= 1700 - /* The minimum and default build targets for VS2012 are Vista and 8, - respectively, unless Update 1 is installed and the v110_xp toolset - is chosen. */ -# ifdef _USING_V110_SDK71_ -# define VS2012_MIN_TARGET 0x0501 /* XP */ -# define VS2012_DEF_TARGET 0x0501 /* XP */ -# else -# define VS2012_MIN_TARGET 0x0600 /* Vista */ -# define VS2012_DEF_TARGET 0x0602 /* 8 */ -# endif - -# ifndef _WIN32_WINNT -# define _WIN32_WINNT VS2012_DEF_TARGET -# endif -# ifndef WINVER -# define WINVER VS2012_DEF_TARGET -# endif -# if (_WIN32_WINNT < VS2012_MIN_TARGET) || (WINVER < VS2012_MIN_TARGET) -# ifdef _USING_V110_SDK71_ -# error VS2012 does not support build targets prior to Windows XP -# else -# error VS2012 does not support build targets prior to Windows Vista -# endif -# endif - /* VS2010 default target settings and minimum build target check. */ -# else - /* VS2010 default build target is Windows 7 (0x0601). - We override default target to be Windows XP. */ -# define VS2010_MIN_TARGET 0x0501 /* XP */ -# define VS2010_DEF_TARGET 0x0501 /* XP */ - -# ifndef _WIN32_WINNT -# define _WIN32_WINNT VS2010_DEF_TARGET -# endif -# ifndef WINVER -# define WINVER VS2010_DEF_TARGET -# endif -# if (_WIN32_WINNT < VS2010_MIN_TARGET) || (WINVER < VS2010_MIN_TARGET) -# error VS2010 does not support build targets prior to Windows XP -# endif -# endif -#endif /* _MSC_VER */ - /* ---------------------------------------------------------------- */ /* HEADER FILES */ /* ---------------------------------------------------------------- */ @@ -93,11 +46,6 @@ #define HAVE_STDBOOL_H 1 #endif -/* Define to 1 if you have the header file. */ -#if defined(_MSC_VER) || defined(__MINGW32__) -#define HAVE_STDINT_H 1 -#endif - /* Define if you have the header file. */ #ifdef __MINGW32__ #define HAVE_SYS_PARAM_H 1 @@ -169,18 +117,9 @@ /* Define if you have the setlocale function. */ #define HAVE_SETLOCALE 1 -/* Define if you have the setmode function. */ -#define HAVE_SETMODE 1 - -/* Define if you have the _setmode function. */ -#define HAVE__SETMODE 1 - /* Define if you have the socket function. */ #define HAVE_SOCKET 1 -/* Define if you have the strdup function. */ -#define HAVE_STRDUP 1 - /* Define if you have the utime function. */ #define HAVE_UTIME 1 @@ -208,9 +147,6 @@ /* Define to the type of arg 1 for send. */ #define SEND_TYPE_ARG1 SOCKET -/* Define to the type qualifier of arg 2 for send. */ -#define SEND_QUAL_ARG2 const - /* Define to the type of arg 2 for send. */ #define SEND_TYPE_ARG2 char * @@ -223,11 +159,6 @@ /* Define to the function return type for send. */ #define SEND_TYPE_RETV int -/* Define to 1 if you have the snprintf function. */ -#if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || defined(__MINGW32__) -#define HAVE_SNPRINTF 1 -#endif - /* Must always use local implementations on Windows. */ /* Define to 1 if you have an IPv6 capable working inet_ntop function. */ /* #undef HAVE_INET_NTOP */ @@ -282,11 +213,6 @@ /* COMPILER SPECIFIC */ /* ---------------------------------------------------------------- */ -/* Define if the compiler supports the 'long long' data type. */ -#if defined(_MSC_VER) || defined(__MINGW32__) -#define HAVE_LONGLONG 1 -#endif - /* Default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */ #if defined(_MSC_VER) || defined(__MINGW32__) # ifndef _USE_32BIT_TIME_T @@ -340,8 +266,7 @@ */ /* Default define to enable threaded asynchronous DNS lookups. */ -#if !defined(USE_SYNC_DNS) && !defined(USE_ARES) && \ - !defined(USE_THREADS_WIN32) +#if !defined(USE_SYNC_DNS) && !defined(USE_ARES) && !defined(USE_THREADS_WIN32) # define USE_THREADS_WIN32 1 #endif @@ -354,15 +279,12 @@ /* ---------------------------------------------------------------- */ #ifndef CURL_WINDOWS_UWP -#undef HAVE_LDAP_URL_PARSE #define HAVE_LDAP_SSL 1 #define USE_WIN32_LDAP 1 -#endif /* Define to use the Windows crypto library. */ -#ifndef CURL_WINDOWS_UWP #define USE_WIN32_CRYPTO -#endif +#endif /* CURL_WINDOWS_UWP */ /* Define to use Unix sockets. */ #define USE_UNIX_SOCKETS diff --git a/vendor/curl/lib/conncache.c b/vendor/curl/lib/conncache.c index 1a3dba7..a865959 100644 --- a/vendor/curl/lib/conncache.c +++ b/vendor/curl/lib/conncache.c @@ -34,9 +34,6 @@ #include "conncache.h" #include "curl_share.h" #include "sigpipe.h" -#include "connect.h" -#include "select.h" -#include "curlx/strparse.h" #define CPOOL_IS_LOCKED(c) ((c) && (c)->locked) @@ -52,7 +49,7 @@ } \ } while(0) -#define CPOOL_UNLOCK(c,d) \ +#define CPOOL_UNLOCK(c, d) \ do { \ if(c) { \ DEBUGASSERT((c)->locked); \ @@ -69,11 +66,6 @@ struct cpool_bundle { char dest[1]; /* destination of bundle, allocated to keep dest_len bytes */ }; -static void cpool_discard_conn(struct cpool *cpool, - struct Curl_easy *data, - struct connectdata *conn, - bool aborted); - static struct cpool_bundle *cpool_bundle_create(const char *dest) { struct cpool_bundle *bundle; @@ -189,26 +181,74 @@ static void cpool_remove_conn(struct cpool *cpool, } } +static void cpool_discard_conn(struct cpool *cpool, + struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + bool done = FALSE; + + DEBUGASSERT(data); + DEBUGASSERT(!data->conn); + DEBUGASSERT(cpool); + DEBUGASSERT(!conn->bits.in_cpool); + + /* + * If this connection is not marked to force-close, leave it open if there + * are other users of it + */ + if(CONN_INUSE(conn) && !aborted) { + CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T + " still in use by %u transfers", conn->connection_id, + conn->attached_xfers); + return; + } + + /* treat the connection as aborted in CONNECT_ONLY situations, we do + * not know what the APP did with it. */ + if(conn->connect_only) + aborted = TRUE; + conn->bits.aborted = aborted; + + /* We do not shutdown dead connections. The term 'dead' can be misleading + * here, as we also mark errored connections/transfers as 'dead'. + * If we do a shutdown for an aborted transfer, the server might think + * it was successful otherwise (for example an ftps: upload). This is + * not what we want. */ + if(aborted) + done = TRUE; + if(!done) { + /* Attempt to shutdown the connection right away. */ + Curl_cshutdn_run_once(cpool->idata, conn, &done); + } + + if(done || !data->multi) + Curl_cshutdn_terminate(cpool->idata, conn, FALSE); + else + Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn); +} + void Curl_cpool_destroy(struct cpool *cpool) { if(cpool && cpool->initialised && cpool->idata) { struct connectdata *conn; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx pipe_ctx; CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections", cpool->share ? "[SHARE] " : "", cpool->num_conn); /* Move all connections to the shutdown list */ - sigpipe_init(&pipe_st); + sigpipe_init(&pipe_ctx); CPOOL_LOCK(cpool, cpool->idata); conn = cpool_get_first(cpool); + if(conn) + sigpipe_apply(cpool->idata, &pipe_ctx); while(conn) { cpool_remove_conn(cpool, conn); - sigpipe_apply(cpool->idata, &pipe_st); cpool_discard_conn(cpool, cpool->idata, conn, FALSE); conn = cpool_get_first(cpool); } CPOOL_UNLOCK(cpool, cpool->idata); - sigpipe_restore(&pipe_st); + sigpipe_restore(&pipe_ctx); Curl_hash_destroy(&cpool->dest2bundle); } } @@ -592,53 +632,6 @@ bool Curl_cpool_find(struct Curl_easy *data, return result; } -static void cpool_discard_conn(struct cpool *cpool, - struct Curl_easy *data, - struct connectdata *conn, - bool aborted) -{ - bool done = FALSE; - - DEBUGASSERT(data); - DEBUGASSERT(!data->conn); - DEBUGASSERT(cpool); - DEBUGASSERT(!conn->bits.in_cpool); - - /* - * If this connection is not marked to force-close, leave it open if there - * are other users of it - */ - if(CONN_INUSE(conn) && !aborted) { - CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T - " still in use by %u transfers", conn->connection_id, - conn->attached_xfers); - return; - } - - /* treat the connection as aborted in CONNECT_ONLY situations, we do - * not know what the APP did with it. */ - if(conn->connect_only) - aborted = TRUE; - conn->bits.aborted = aborted; - - /* We do not shutdown dead connections. The term 'dead' can be misleading - * here, as we also mark errored connections/transfers as 'dead'. - * If we do a shutdown for an aborted transfer, the server might think - * it was successful otherwise (for example an ftps: upload). This is - * not what we want. */ - if(aborted) - done = TRUE; - if(!done) { - /* Attempt to shutdown the connection right away. */ - Curl_cshutdn_run_once(cpool->idata, conn, &done); - } - - if(done || !data->multi) - Curl_cshutdn_terminate(cpool->idata, conn, FALSE); - else - Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn); -} - void Curl_conn_terminate(struct Curl_easy *data, struct connectdata *conn, bool aborted) @@ -808,7 +801,7 @@ static int cpool_do_conn(struct Curl_easy *data, struct connectdata *conn, void *param) { struct cpool_do_conn_ctx *dctx = param; - (void)data; + if(conn->connection_id == dctx->id) { dctx->cb(conn, data, dctx->cbdata); return 1; @@ -858,7 +851,6 @@ static int cpool_mark_stale(struct Curl_easy *data, static int cpool_reap_no_reuse(struct Curl_easy *data, struct connectdata *conn, void *param) { - (void)data; (void)param; if(!CONN_INUSE(conn) && conn->bits.no_reuse) { Curl_conn_terminate(data, conn, FALSE); diff --git a/vendor/curl/lib/conncache.h b/vendor/curl/lib/conncache.h index 9b392af..d8626ae 100644 --- a/vendor/curl/lib/conncache.h +++ b/vendor/curl/lib/conncache.h @@ -68,7 +68,7 @@ void Curl_cpool_init(struct cpool *cpool, size_t size); /* Destroy all connections and free all members */ -void Curl_cpool_destroy(struct cpool *connc); +void Curl_cpool_destroy(struct cpool *cpool); /* Init the transfer to be used within its connection pool. * Assigns `data->id`. */ diff --git a/vendor/curl/lib/connect.c b/vendor/curl/lib/connect.c index 6e17834..78c9370 100644 --- a/vendor/curl/lib/connect.c +++ b/vendor/curl/lib/connect.c @@ -61,7 +61,7 @@ #include "multiif.h" #include "curlx/inet_ntop.h" #include "curlx/strparse.h" -#include "vtls/vtls.h" /* for vtsl cfilters */ +#include "vtls/vtls.h" /* for vtls cfilters */ #include "progress.h" #include "conncache.h" #include "multihandle.h" @@ -100,25 +100,27 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *cstr) * transfer/connection. If the value is 0, there is no timeout (ie there is * infinite time left). If the value is negative, the timeout time has already * elapsed. - * @param data the transfer to check on - * @param duringconnect TRUE iff connect timeout is also taken into account. * @unittest: 1303 */ timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, - const struct curltime *pnow, - bool duringconnect) + const struct curltime *pnow) { timediff_t timeleft_ms = 0; timediff_t ctimeleft_ms = 0; - timediff_t ctimeout_ms; - - /* The duration of a connect and the total transfer are calculated from two - different time-stamps. It can end up with the total timeout being reached - before the connect timeout expires and we must acknowledge whichever - timeout that is reached first. The total timeout is set per entire - operation, while the connect timeout is set per connect. */ - if((!data->set.timeout || data->set.connect_only) && !duringconnect) + + if(Curl_shutdown_started(data, FIRSTSOCKET)) + return Curl_shutdown_timeleft(data, data->conn, FIRSTSOCKET); + else if(Curl_is_connecting(data)) { + timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ? + data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT; + ctimeleft_ms = ctimeout_ms - + curlx_ptimediff_ms(pnow, &data->progress.t_startsingle); + if(!ctimeleft_ms) + ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ + } + else if(!data->set.timeout || data->set.connect_only) { return 0; /* no timeout in place or checked, return "no limit" */ + } if(data->set.timeout) { timeleft_ms = data->set.timeout - @@ -127,25 +129,16 @@ timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ } - if(!duringconnect) - return timeleft_ms; /* no connect check, this is it */ - ctimeout_ms = (data->set.connecttimeout > 0) ? - data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT; - ctimeleft_ms = ctimeout_ms - - curlx_ptimediff_ms(pnow, &data->progress.t_startsingle); if(!ctimeleft_ms) - ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ - if(!timeleft_ms) - return ctimeleft_ms; /* no general timeout, this is it */ - - /* return minimal time left or max amount already expired */ - return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms; + return timeleft_ms; + else if(!timeleft_ms) + return ctimeleft_ms; + return CURLMIN(ctimeleft_ms, timeleft_ms); } -timediff_t Curl_timeleft_ms(struct Curl_easy *data, - bool duringconnect) +timediff_t Curl_timeleft_ms(struct Curl_easy *data) { - return Curl_timeleft_now_ms(data, Curl_pgrs_now(data), duringconnect); + return Curl_timeleft_now_ms(data, Curl_pgrs_now(data)); } void Curl_shutdown_start(struct Curl_easy *data, int sockindex, @@ -162,6 +155,8 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex, /* Set a timer, unless we operate on the admin handle */ if(data->mid) Curl_expire_ex(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN); + CURL_TRC_M(data, "shutdown start on%s connection", + sockindex ? " secondary" : ""); } timediff_t Curl_shutdown_timeleft(struct Curl_easy *data, @@ -204,8 +199,11 @@ void Curl_shutdown_clear(struct Curl_easy *data, int sockindex) bool Curl_shutdown_started(struct Curl_easy *data, int sockindex) { - struct curltime *pt = &data->conn->shutdown.start[sockindex]; - return (pt->tv_sec > 0) || (pt->tv_usec > 0); + if(data->conn) { + struct curltime *pt = &data->conn->shutdown.start[sockindex]; + return (pt->tv_sec > 0) || (pt->tv_usec > 0); + } + return FALSE; } /* retrieves ip address and port from a sockaddr structure. note it calls @@ -299,7 +297,7 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, */ void Curl_conncontrol(struct connectdata *conn, int ctrl /* see defines in header */ -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) , const char *reason #endif ) @@ -309,7 +307,7 @@ void Curl_conncontrol(struct connectdata *conn, associated with a transfer. */ bool closeit, is_multiplex; DEBUGASSERT(conn); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) (void)reason; /* useful for debugging */ #endif is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); @@ -317,7 +315,7 @@ void Curl_conncontrol(struct connectdata *conn, ((ctrl == CONNCTRL_STREAM) && !is_multiplex); if((ctrl == CONNCTRL_STREAM) && is_multiplex) ; /* stream signal on multiplex conn never affects close state */ - else if((bit)closeit != conn->bits.close) { + else if((curl_bit)closeit != conn->bits.close) { conn->bits.close = closeit; /* the only place in the source code that should assign this bit */ } @@ -428,8 +426,8 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, #ifdef USE_SSL if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || (ctx->ssl_mode != CURL_CF_SSL_DISABLE && - cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */ - && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ + cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ result = Curl_cf_ssl_insert_after(cf, data); if(result) return result; @@ -465,7 +463,6 @@ static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_setup_ctx *ctx = cf->ctx; - (void)data; CURL_TRC_CF(data, cf, "destroy"); Curl_safefree(ctx); } @@ -564,7 +561,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, CURLcode result = CURLE_OK; DEBUGASSERT(data); - DEBUGASSERT(conn->handler); + DEBUGASSERT(conn->scheme); DEBUGASSERT(dns); Curl_resolv_unlink(data, &data->state.dns[sockindex]); @@ -572,7 +569,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, #ifndef CURL_DISABLE_HTTP if(!conn->cfilter[sockindex] && - conn->handler->protocol == CURLPROTO_HTTPS) { + conn->scheme->protocol == CURLPROTO_HTTPS) { DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); result = Curl_cf_https_setup(data, conn, sockindex); if(result) diff --git a/vendor/curl/lib/connect.h b/vendor/curl/lib/connect.h index 01e9dfc..071b432 100644 --- a/vendor/curl/lib/connect.h +++ b/vendor/curl/lib/connect.h @@ -32,15 +32,13 @@ struct ip_quadruple; struct Curl_str; enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); -enum alpnid Curl_str2alpnid(const struct Curl_str *str); +enum alpnid Curl_str2alpnid(const struct Curl_str *cstr); /* generic function that returns how much time there is left to run, according to the timeouts set */ -timediff_t Curl_timeleft_ms(struct Curl_easy *data, - bool duringconnect); +timediff_t Curl_timeleft_ms(struct Curl_easy *data); timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, - const struct curltime *pnow, - bool duringconnect); + const struct curltime *pnow); #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ @@ -78,7 +76,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, char *addr, uint16_t *port); /* - * Curl_conncontrol() marks the end of a connection/stream. The 'closeit' + * Curl_conncontrol() marks the end of a connection/stream. The 'ctrl' * argument specifies if it is the end of a connection or a stream. * * For stream-based protocols (such as HTTP/2), a stream close will not cause @@ -94,17 +92,17 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, #define CONNCTRL_STREAM 2 void Curl_conncontrol(struct connectdata *conn, - int closeit -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + int ctrl +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) , const char *reason #endif ); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) #define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM, y) #define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y) #define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y) -#else /* if !DEBUGBUILD || CURL_DISABLE_VERBOSE_STRINGS */ +#else /* !DEBUGBUILD || !CURLVERBOSE */ #define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM) #define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION) #define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP) diff --git a/vendor/curl/lib/content_encoding.c b/vendor/curl/lib/content_encoding.c index 70fb569..aa35da8 100644 --- a/vendor/curl/lib/content_encoding.c +++ b/vendor/curl/lib/content_encoding.c @@ -193,7 +193,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, done = FALSE; break; case Z_BUF_ERROR: - /* No more data to flush: just exit loop. */ + /* No more data to flush: exit loop. */ break; case Z_STREAM_END: result = process_trailer(data, zp); @@ -600,9 +600,7 @@ static const struct Curl_cwtype * const general_unencoders[] = { /* supported content decoders only for transfer encodings */ static const struct Curl_cwtype * const transfer_unencoders[] = { -#ifndef CURL_DISABLE_HTTP &Curl_httpchunk_unencoder, -#endif NULL }; @@ -624,6 +622,9 @@ char *Curl_get_content_encodings(void) result = curlx_dyn_add(&enc, ce->name); } } + if(!result && !curlx_dyn_len(&enc)) + result = curlx_dyn_add(&enc, CONTENT_ENCODING_DEFAULT); + if(!result) return curlx_dyn_ptr(&enc); return NULL; @@ -679,8 +680,8 @@ static const struct Curl_cwtype *find_unencode_writer(const char *name, for(cep = transfer_unencoders; *cep; cep++) { const struct Curl_cwtype *ce = *cep; if((curl_strnequal(name, ce->name, len) && !ce->name[len]) || - (ce->alias && curl_strnequal(name, ce->alias, len) - && !ce->alias[len])) + (ce->alias && curl_strnequal(name, ce->alias, len) && + !ce->alias[len])) return ce; } } diff --git a/vendor/curl/lib/cookie.c b/vendor/curl/lib/cookie.c index c807222..4d53cc4 100644 --- a/vendor/curl/lib/cookie.c +++ b/vendor/curl/lib/cookie.c @@ -36,9 +36,8 @@ #include "curl_get_line.h" #include "curl_memrchr.h" #include "parsedate.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "llist.h" -#include "bufref.h" #include "curlx/strparse.h" /* number of seconds in 400 days */ @@ -245,7 +244,7 @@ static char *sanitize_cookie_path(const char *cookie_path, size_t len) if(len > 1 && cookie_path[len - 1] == '/') len--; - return Curl_memdup0(cookie_path, len); + return curlx_memdup0(cookie_path, len); } /* @@ -264,7 +263,7 @@ static CURLcode strstore(char **str, const char *newstr, size_t len) len++; newstr = ""; } - *str = Curl_memdup0(newstr, len); + *str = curlx_memdup0(newstr, len); if(!*str) return CURLE_OUT_OF_MEMORY; return CURLE_OK; @@ -288,10 +287,10 @@ static void remove_expired(struct CookieInfo *ci) /* * If the earliest expiration timestamp in the jar is in the future we can * skip scanning the whole jar and instead exit early as there will not be - * any cookies to evict. If we need to evict however, reset the - * next_expiration counter in order to track the next one. In case the - * recorded first expiration is the max offset, then perform the safe - * fallback of checking all cookies. + * any cookies to evict. If we need to evict, reset the next_expiration + * counter in order to track the next one. In case the recorded first + * expiration is the max offset, then perform the safe fallback of checking + * all cookies. */ if(now < ci->next_expiration && ci->next_expiration != CURL_OFF_T_MAX) @@ -331,7 +330,7 @@ static bool bad_domain(const char *domain, size_t len) return FALSE; else { /* there must be a dot present, but that dot must not be a trailing dot */ - char *dot = memchr(domain, '.', len); + const char *dot = memchr(domain, '.', len); if(dot) { size_t i = dot - domain; if((len - i) > 1) @@ -367,15 +366,14 @@ static bool invalid_octets(const char *ptr, size_t len) /* The maximum length we accept a date string for the 'expire' keyword. The standard date formats are within the 30 bytes range. This adds an extra - margin just to make sure it realistically works with what is used out - there. + margin to make sure it realistically works with what is used out there. */ #define MAX_DATE_LENGTH 80 -#define COOKIE_NAME 0 -#define COOKIE_VALUE 1 +#define COOKIE_NAME 0 +#define COOKIE_VALUE 1 #define COOKIE_DOMAIN 2 -#define COOKIE_PATH 3 +#define COOKIE_PATH 3 #define COOKIE_PIECES 4 /* the list above */ @@ -398,7 +396,7 @@ static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, /* No path was given in the header line, set the default */ const char *endslash = strrchr(path, '/'); if(endslash) - plen = (endslash - path + 1); /* include end slash */ + plen = endslash - path + 1; /* include end slash */ else plen = strlen(path); } @@ -694,7 +692,7 @@ static CURLcode parse_netscape(struct Cookie *co, ptr++; len--; } - co->domain = Curl_memdup0(ptr, len); + co->domain = curlx_memdup0(ptr, len); if(!co->domain) return CURLE_OUT_OF_MEMORY; break; @@ -737,7 +735,7 @@ static CURLcode parse_netscape(struct Cookie *co, return CURLE_OK; break; case 5: - co->name = Curl_memdup0(ptr, len); + co->name = curlx_memdup0(ptr, len); if(!co->name) return CURLE_OUT_OF_MEMORY; else { @@ -749,7 +747,7 @@ static CURLcode parse_netscape(struct Cookie *co, } break; case 6: - co->value = Curl_memdup0(ptr, len); + co->value = curlx_memdup0(ptr, len); if(!co->value) return CURLE_OUT_OF_MEMORY; break; @@ -783,7 +781,7 @@ static bool is_public_suffix(struct Curl_easy *data, * dereference it. */ DEBUGF(infof(data, "PSL check set-cookie '%s' for domain=%s in %s", - co->name, co->domain, domain)); + co->name, co->domain, domain)); if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { bool acceptable = FALSE; char lcase[256]; @@ -814,7 +812,7 @@ static bool is_public_suffix(struct Curl_easy *data, (void)co; (void)domain; DEBUGF(infof(data, "NO PSL to check set-cookie '%s' for domain=%s in %s", - co->name, co->domain, domain)); + co->name, co->domain, domain)); #endif return FALSE; } @@ -1008,7 +1006,7 @@ Curl_cookie_add(struct Curl_easy *data, goto fail; /* clone the stack struct into heap */ - co = Curl_memdup(&comem, sizeof(comem)); + co = curlx_memdup(&comem, sizeof(comem)); if(!co) { co = &comem; result = CURLE_OUT_OF_MEMORY; @@ -1169,7 +1167,7 @@ CURLcode Curl_cookie_loadfiles(struct Curl_easy *data) data->state.cookie_engine = TRUE; while(list) { result = cookie_load(data, list->data, data->cookies, - data->set.cookiesession); + (bool)data->set.cookiesession); if(result) break; list = list->next; @@ -1234,7 +1232,7 @@ static int cookie_sort_ct(const void *p1, const void *p2) bool Curl_secure_context(struct connectdata *conn, const char *host) { - return conn->handler->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS) || + return conn->scheme->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS) || curl_strequal("localhost", host) || !strcmp(host, "127.0.0.1") || !strcmp(host, "::1"); @@ -1315,8 +1313,8 @@ CURLcode Curl_cookie_getlist(struct Curl_easy *data, if(matches) { /* * Now we need to make sure that if there is a name appearing more than - * once, the longest specified path version comes first. To make this - * the swiftest way, we just sort them all based on path length. + * once, the longest specified path version comes first. To make this the + * swiftest way, we sort them all based on path length. */ struct Cookie **array; size_t i; diff --git a/vendor/curl/lib/cookie.h b/vendor/curl/lib/cookie.h index cbc7b74..fabd04d 100644 --- a/vendor/curl/lib/cookie.h +++ b/vendor/curl/lib/cookie.h @@ -111,15 +111,18 @@ struct connectdata; bool Curl_secure_context(struct connectdata *conn, const char *host); CURLcode Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *c, bool header, - bool noexpiry, const char *lineptr, - const char *domain, const char *path, + struct CookieInfo *ci, + bool httpheader, + bool noexpire, + const char *lineptr, + const char *domain, + const char *path, bool secure) WARN_UNUSED_RESULT; CURLcode Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn, bool *okay, const char *host, struct Curl_llist *list) WARN_UNUSED_RESULT; -void Curl_cookie_clearall(struct CookieInfo *cookies); -void Curl_cookie_clearsess(struct CookieInfo *cookies); +void Curl_cookie_clearall(struct CookieInfo *ci); +void Curl_cookie_clearsess(struct CookieInfo *ci); #if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES) #define Curl_cookie_list(x) NULL @@ -130,7 +133,7 @@ void Curl_cookie_clearsess(struct CookieInfo *cookies); #define Curl_flush_cookies(x, y) Curl_nop_stmt #else void Curl_flush_cookies(struct Curl_easy *data, bool cleanup); -void Curl_cookie_cleanup(struct CookieInfo *c); +void Curl_cookie_cleanup(struct CookieInfo *ci); struct CookieInfo *Curl_cookie_init(void); struct curl_slist *Curl_cookie_list(struct Curl_easy *data); CURLcode Curl_cookie_loadfiles(struct Curl_easy *data) WARN_UNUSED_RESULT; diff --git a/vendor/curl/lib/cshutdn.c b/vendor/curl/lib/cshutdn.c index 84e5b2a..308f2aa 100644 --- a/vendor/curl/lib/cshutdn.c +++ b/vendor/curl/lib/cshutdn.c @@ -43,14 +43,14 @@ static void cshutdn_run_conn_handler(struct Curl_easy *data, { if(!conn->bits.shutdown_handler) { - if(conn->handler && conn->handler->disconnect) { + if(conn->scheme && conn->scheme->run->disconnect) { /* Some disconnect handlers do a blocking wait on server responses. * FTP/IMAP/SMTP and SFTP are among them. When using the internal * handle, set an overall short timeout so we do not hang for the * default 120 seconds. */ if(data->state.internal) { data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS; - (void)Curl_pgrsTime(data, TIMER_STARTOP); + Curl_pgrsTime(data, TIMER_STARTOP); } /* This is set if protocol-specific cleanups should be made */ @@ -59,7 +59,7 @@ static void cshutdn_run_conn_handler(struct Curl_easy *data, conn->connection_id, conn->bits.aborted)); /* There are protocol handlers that block on retrieving * server responses here (FTP). Set a short timeout. */ - conn->handler->disconnect(data, conn, conn->bits.aborted); + conn->scheme->run->disconnect(data, conn, (bool)conn->bits.aborted); } conn->bits.shutdown_handler = TRUE; @@ -76,6 +76,10 @@ static void cshutdn_run_once(struct Curl_easy *data, /* We expect to be attached when called */ DEBUGASSERT(data->conn == conn); + if(!Curl_shutdown_started(data, FIRSTSOCKET)) { + Curl_shutdown_start(data, FIRSTSOCKET, 0); + } + cshutdn_run_conn_handler(data, conn); if(conn->bits.shutdown_filters) { @@ -176,13 +180,13 @@ static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn, } if(e) { - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; conn = Curl_node_elem(e); Curl_node_remove(e); - sigpipe_init(&pipe_st); - sigpipe_apply(data, &pipe_st); + sigpipe_init(&sigpipe_ctx); + sigpipe_apply(data, &sigpipe_ctx); Curl_cshutdn_terminate(data, conn, FALSE); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); return TRUE; } return FALSE; @@ -222,7 +226,8 @@ static CURLcode cshutdn_wait(struct cshutdn *cshutdn, } static void cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_sigpipe_ctx *sigpipe_ctx) { struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list); struct Curl_llist_node *enext; @@ -235,6 +240,7 @@ static void cshutdn_perform(struct cshutdn *cshutdn, CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections", Curl_llist_count(&cshutdn->list)); + sigpipe_apply(data, sigpipe_ctx); while(e) { enext = Curl_node_next(e); conn = Curl_node_elem(e); @@ -263,20 +269,19 @@ static void cshutdn_terminate_all(struct cshutdn *cshutdn, { struct curltime started = *Curl_pgrs_now(data); struct Curl_llist_node *e; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; DEBUGASSERT(cshutdn); DEBUGASSERT(data); CURL_TRC_M(data, "[SHUTDOWN] shutdown all"); - sigpipe_init(&pipe_st); - sigpipe_apply(data, &pipe_st); + sigpipe_init(&sigpipe_ctx); while(Curl_llist_head(&cshutdn->list)) { timediff_t spent_ms; int remain_ms; - cshutdn_perform(cshutdn, data); + cshutdn_perform(cshutdn, data, &sigpipe_ctx); if(!Curl_llist_head(&cshutdn->list)) { CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly"); @@ -308,7 +313,7 @@ static void cshutdn_terminate_all(struct cshutdn *cshutdn, } DEBUGASSERT(!Curl_llist_count(&cshutdn->list)); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); } int Curl_cshutdn_init(struct cshutdn *cshutdn, @@ -326,7 +331,7 @@ void Curl_cshutdn_destroy(struct cshutdn *cshutdn, { if(cshutdn->initialised && data) { int timeout_ms = 0; - /* Just for testing, run graceful shutdown */ + /* for testing, run graceful shutdown */ #ifdef DEBUGBUILD { const char *p = getenv("CURL_GRACEFUL_SHUTDOWN"); @@ -396,8 +401,8 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn, /* Add the connection to our shutdown list for non-blocking shutdown * during multi processing. */ - if(max_total > 0 && (max_total <= - (conns_in_pool + Curl_llist_count(&cshutdn->list)))) { + if(max_total > 0 && + (max_total <= (conns_in_pool + Curl_llist_count(&cshutdn->list)))) { CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection " "due to connection limit of %zu", max_total); cshutdn_destroy_oldest(cshutdn, data, NULL); @@ -418,38 +423,11 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn, conn->connection_id, Curl_llist_count(&cshutdn->list)); } -static void cshutdn_multi_socket(struct cshutdn *cshutdn, - struct Curl_easy *data, - curl_socket_t s) -{ - struct Curl_llist_node *e; - struct connectdata *conn; - bool done; - - DEBUGASSERT(cshutdn->multi->socket_cb); - e = Curl_llist_head(&cshutdn->list); - while(e) { - conn = Curl_node_elem(e); - if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) { - Curl_cshutdn_run_once(data, conn, &done); - if(done || cshutdn_update_ev(cshutdn, data, conn)) { - Curl_node_remove(e); - Curl_cshutdn_terminate(data, conn, FALSE); - } - break; - } - e = Curl_node_next(e); - } -} - void Curl_cshutdn_perform(struct cshutdn *cshutdn, struct Curl_easy *data, - curl_socket_t s) + struct Curl_sigpipe_ctx *sigpipe_ctx) { - if((s == CURL_SOCKET_TIMEOUT) || (!cshutdn->multi->socket_cb)) - cshutdn_perform(cshutdn, data); - else - cshutdn_multi_socket(cshutdn, data, s); + cshutdn_perform(cshutdn, data, sigpipe_ctx); } /* return fd_set info about the shutdown connections */ @@ -480,17 +458,10 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, curl_socket_t sock = ps.sockets[i]; if(!FDSET_SOCK(sock)) continue; -#ifdef __DJGPP__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warith-conversion" -#endif if(ps.actions[i] & CURL_POLL_IN) FD_SET(sock, read_fd_set); if(ps.actions[i] & CURL_POLL_OUT) FD_SET(sock, write_fd_set); -#ifdef __DJGPP__ -#pragma GCC diagnostic pop -#endif if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) && ((int)sock > *maxfd)) *maxfd = (int)sock; diff --git a/vendor/curl/lib/cshutdn.h b/vendor/curl/lib/cshutdn.h index 0a15d39..9030474 100644 --- a/vendor/curl/lib/cshutdn.h +++ b/vendor/curl/lib/cshutdn.h @@ -30,6 +30,7 @@ struct curl_pollfds; struct Curl_waitfds; struct Curl_multi; struct Curl_share; +struct Curl_sigpipe_ctx; /* Run the shutdown of the connection once. * Will shortly attach/detach `data` to `conn` while doing so. @@ -40,12 +41,12 @@ void Curl_cshutdn_run_once(struct Curl_easy *data, bool *done); /* Terminates the connection, e.g. closes and destroys it. - * If `run_shutdown` is TRUE, the shutdown will be run once before + * If `do_shutdown` is TRUE, the shutdown will be run once before * terminating it. * Takes ownership of `conn`. */ void Curl_cshutdn_terminate(struct Curl_easy *data, struct connectdata *conn, - bool run_shutdown); + bool do_shutdown); /* A `cshutdown` is always owned by a multi handle to maintain * the connections to be shut down. It registers timers and @@ -97,10 +98,9 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, fd_set *read_fd_set, fd_set *write_fd_set, int *maxfd); -/* Run shut down connections using socket. If socket is CURL_SOCKET_TIMEOUT, - * run maintenance on all connections. */ +/* Run maintenance on all connections. */ void Curl_cshutdn_perform(struct cshutdn *cshutdn, struct Curl_easy *data, - curl_socket_t s); + struct Curl_sigpipe_ctx *sigpipe_ctx); #endif /* HEADER_CURL_CSHUTDN_H */ diff --git a/vendor/curl/lib/curl_addrinfo.c b/vendor/curl/lib/curl_addrinfo.c index 61c8b18..43d398d 100644 --- a/vendor/curl/lib/curl_addrinfo.c +++ b/vendor/curl/lib/curl_addrinfo.c @@ -62,9 +62,9 @@ #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) /* workaround icc 9.1 optimizer issue */ -# define vqualifier volatile +# define vqualifier volatile #else -# define vqualifier +# define vqualifier #endif void Curl_freeaddrinfo(struct Curl_addrinfo *cahead) @@ -115,8 +115,8 @@ int Curl_getaddrinfo_ex(const char *nodename, for(ai = aihead; ai != NULL; ai = ai->ai_next) { size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0; - /* ignore elements with unsupported address family, */ - /* settle family-specific sockaddr structure size. */ + /* ignore elements with unsupported address family, + settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); #ifdef USE_IPV6 @@ -140,8 +140,8 @@ int Curl_getaddrinfo_ex(const char *nodename, break; } - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ + /* copy each structure member individually, member ordering, + size, or padding might be different for each platform. */ ca->ai_flags = ai->ai_flags; ca->ai_family = ai->ai_family; @@ -404,19 +404,19 @@ static CURLcode ip2addr(struct Curl_addrinfo **addrp, int af, * Given an IPv4 or IPv6 dotted string address, this converts it to a proper * allocated Curl_addrinfo struct and returns it. */ -CURLcode Curl_str2addr(const char *address, int port, +CURLcode Curl_str2addr(const char *dotted, int port, struct Curl_addrinfo **addrp) { struct in_addr in; - if(curlx_inet_pton(AF_INET, address, &in) > 0) + if(curlx_inet_pton(AF_INET, dotted, &in) > 0) /* This is a dotted IP address 123.123.123.123-style */ - return ip2addr(addrp, AF_INET, &in, address, port); + return ip2addr(addrp, AF_INET, &in, dotted, port); #ifdef USE_IPV6 { struct in6_addr in6; - if(curlx_inet_pton(AF_INET6, address, &in6) > 0) + if(curlx_inet_pton(AF_INET6, dotted, &in6) > 0) /* This is a dotted IPv6 address ::1-style */ - return ip2addr(addrp, AF_INET6, &in6, address, port); + return ip2addr(addrp, AF_INET6, &in6, dotted, port); } #endif return CURLE_BAD_FUNCTION_ARGUMENT; /* bad input format */ @@ -485,7 +485,7 @@ struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, } #endif -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \ defined(HAVE_FREEADDRINFO) /* * curl_dbg_freeaddrinfo() @@ -515,9 +515,9 @@ void curl_dbg_freeaddrinfo(struct addrinfo *freethis, freeaddrinfo(freethis); #endif } -#endif /* CURLDEBUG && HAVE_FREEADDRINFO */ +#endif /* CURL_MEMDEBUG && HAVE_FREEADDRINFO */ -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) /* * curl_dbg_getaddrinfo() * @@ -553,7 +553,7 @@ int curl_dbg_getaddrinfo(const char *hostname, curl_dbg_log("ADDR %s:%d getaddrinfo() failed\n", source, line); return res; } -#endif /* CURLDEBUG && HAVE_GETADDRINFO */ +#endif /* CURL_MEMDEBUG && HAVE_GETADDRINFO */ #if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS) /* diff --git a/vendor/curl/lib/curl_addrinfo.h b/vendor/curl/lib/curl_addrinfo.h index ca80f3a..f464b5e 100644 --- a/vendor/curl/lib/curl_addrinfo.h +++ b/vendor/curl/lib/curl_addrinfo.h @@ -38,7 +38,6 @@ #ifdef __VMS # include # include -# include #endif /* @@ -73,20 +72,21 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port); #endif bool Curl_is_ipaddr(const char *address); -CURLcode Curl_str2addr(const char *dotted, int port, struct Curl_addrinfo **); +CURLcode Curl_str2addr(const char *dotted, int port, + struct Curl_addrinfo **addrp); #ifdef USE_UNIX_SOCKETS struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, bool abstract); #endif -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \ defined(HAVE_FREEADDRINFO) void curl_dbg_freeaddrinfo(struct addrinfo *freethis, int line, const char *source); #endif -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) int curl_dbg_getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result, int line, diff --git a/vendor/curl/lib/curl_config-cmake.h.in b/vendor/curl/lib/curl_config-cmake.h.in index d8d9d7e..1dcab9d 100644 --- a/vendor/curl/lib/curl_config-cmake.h.in +++ b/vendor/curl/lib/curl_config-cmake.h.in @@ -34,6 +34,9 @@ /* Default SSL backend */ #cmakedefine CURL_DEFAULT_SSL_BACKEND "${CURL_DEFAULT_SSL_BACKEND}" +/* Use native CA store */ +#cmakedefine CURL_CA_NATIVE 1 + /* disables alt-svc */ #cmakedefine CURL_DISABLE_ALTSVC 1 @@ -160,9 +163,6 @@ /* disables TFTP */ #cmakedefine CURL_DISABLE_TFTP 1 -/* disables curl_easy_setopt()/curl_easy_getinfo() type checking */ -#cmakedefine CURL_DISABLE_TYPECHECK 1 - /* disables verbose strings */ #cmakedefine CURL_DISABLE_VERBOSE_STRINGS 1 @@ -324,6 +324,12 @@ /* MIT Kerberos version */ #cmakedefine CURL_KRB5_VERSION ${CURL_KRB5_VERSION} +/* BoringSSL version */ +#cmakedefine CURL_BORINGSSL_VERSION ${CURL_BORINGSSL_VERSION} + +/* Patch stamp */ +#cmakedefine CURL_PATCHSTAMP ${CURL_PATCHSTAMP} + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IFADDRS_H 1 @@ -394,15 +400,9 @@ /* Define to 1 if you have a working localtime_r function. */ #cmakedefine HAVE_LOCALTIME_R 1 -/* Define to 1 if the compiler supports the 'long long' data type. */ -#cmakedefine HAVE_LONGLONG 1 - /* Define to 1 if you have the 'suseconds_t' data type. */ #cmakedefine HAVE_SUSECONDS_T 1 -/* Define to 1 if you have the MSG_NOSIGNAL flag. */ -#cmakedefine HAVE_MSG_NOSIGNAL 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETDB_H 1 @@ -472,9 +472,6 @@ /* Define to 1 if you have the sendmmsg function. */ #cmakedefine HAVE_SENDMMSG 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_STDINT_H 1 - /* Define to 1 if you have the 'fsetxattr' function. */ #cmakedefine HAVE_FSETXATTR 1 @@ -487,12 +484,6 @@ /* Define to 1 if you have the `setlocale' function. */ #cmakedefine HAVE_SETLOCALE 1 -/* Define to 1 if you have the `setmode' function. */ -#cmakedefine HAVE_SETMODE 1 - -/* Define to 1 if you have the `_setmode' function. */ -#cmakedefine HAVE__SETMODE 1 - /* Define to 1 if you have the `setrlimit' function. */ #cmakedefine HAVE_SETRLIMIT 1 @@ -538,9 +529,6 @@ /* Define to 1 if you have the strcmpi function. */ #cmakedefine HAVE_STRCMPI 1 -/* Define to 1 if you have the strdup function. */ -#cmakedefine HAVE_STRDUP 1 - /* Define to 1 if you have the strerror_r function. */ #cmakedefine HAVE_STRERROR_R 1 @@ -626,12 +614,12 @@ #cmakedefine CURL_OS ${CURL_OS} /* - Note: SIZEOF_* variables are fetched with CMake through check_type_size(). - As per CMake documentation on CheckTypeSize, C preprocessor code is - generated by CMake into SIZEOF_*_CODE. This is what we use in the - following statements. + Note: SIZEOF_* variables are fetched with CMake through check_type_size(). + As per CMake documentation on CheckTypeSize, C preprocessor code is + generated by CMake into SIZEOF_*_CODE. This is what we use in the + following statements. - Reference: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html + Reference: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html */ /* The size of `int', as computed by sizeof. */ @@ -640,9 +628,6 @@ ${SIZEOF_INT_CODE} /* The size of `long', as computed by sizeof. */ ${SIZEOF_LONG_CODE} -/* The size of `long long', as computed by sizeof. */ -${SIZEOF_LONG_LONG_CODE} - /* The size of `off_t', as computed by sizeof. */ ${SIZEOF_OFF_T_CODE} @@ -740,34 +725,31 @@ ${SIZEOF_TIME_T_CODE} automatically */ #cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1 -/* to enable NGHTTP2 */ +/* to enable NGHTTP2 */ #cmakedefine USE_NGHTTP2 1 /* to enable NGTCP2 */ #cmakedefine USE_NGTCP2 1 -/* to enable NGHTTP3 */ +/* to enable NGHTTP3 */ #cmakedefine USE_NGHTTP3 1 /* to enable quiche */ #cmakedefine USE_QUICHE 1 -/* to enable openssl + nghttp3 */ -#cmakedefine USE_OPENSSL_QUIC 1 - /* to enable openssl + ngtcp2 + nghttp3 */ #cmakedefine OPENSSL_QUIC_API2 1 /* Define to 1 if you have the quiche_conn_set_qlog_fd function. */ #cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1 -/* if Unix domain sockets are enabled */ +/* if Unix domain sockets are enabled */ #cmakedefine USE_UNIX_SOCKETS 1 /* to enable SSPI support */ #cmakedefine USE_WINDOWS_SSPI 1 -/* to enable Windows SSL */ +/* to enable Windows SSL */ #cmakedefine USE_SCHANNEL 1 /* if Watt-32 is in use */ diff --git a/vendor/curl/lib/curl_fnmatch.c b/vendor/curl/lib/curl_fnmatch.c index 8e35ede..dde956c 100644 --- a/vendor/curl/lib/curl_fnmatch.c +++ b/vendor/curl/lib/curl_fnmatch.c @@ -251,8 +251,8 @@ static int setcharset(const unsigned char **p, unsigned char *charset) static int loop(const unsigned char *pattern, const unsigned char *string, int maxstars) { - const unsigned char *p = (const unsigned char *)pattern; - const unsigned char *s = (const unsigned char *)string; + const unsigned char *p = pattern; + const unsigned char *s = string; unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 }; for(;;) { diff --git a/vendor/curl/lib/curl_fopen.c b/vendor/curl/lib/curl_fopen.c index 9559c0d..b7a67aa 100644 --- a/vendor/curl/lib/curl_fopen.c +++ b/vendor/curl/lib/curl_fopen.c @@ -23,7 +23,7 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ +#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ !defined(CURL_DISABLE_HSTS) #include "urldata.h" @@ -89,7 +89,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, unsigned char randbuf[41]; char *tempstore = NULL; #ifndef _WIN32 - struct_stat sb; + curlx_struct_stat sb; #endif int fd = -1; char *dir = NULL; @@ -99,7 +99,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, *fh = curlx_fopen(filename, FOPEN_WRITETEXT); if(!*fh) goto fail; - if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { + if(curlx_fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { return CURLE_OK; } curlx_fclose(*fh); @@ -125,8 +125,8 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, result = CURLE_WRITE_ERROR; #ifdef _WIN32 - fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL, - S_IREAD | S_IWRITE); + fd = curlx_open(tempstore, _O_WRONLY | _O_CREAT | _O_EXCL, + _S_IREAD | _S_IWRITE); #elif (defined(ANDROID) || defined(__ANDROID__)) && \ (defined(__i386__) || defined(__arm__)) fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL, @@ -147,7 +147,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, fail: if(fd != -1) { - close(fd); + curlx_close(fd); unlink(tempstore); } diff --git a/vendor/curl/lib/curl_get_line.c b/vendor/curl/lib/curl_get_line.c index 46c1287..85b3525 100644 --- a/vendor/curl/lib/curl_get_line.c +++ b/vendor/curl/lib/curl_get_line.c @@ -23,13 +23,11 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ +#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \ !defined(CURL_DISABLE_HSTS) || !defined(CURL_DISABLE_NETRC) #include "curl_get_line.h" -#define appendnl(b) curlx_dyn_addn(buf, "\n", 1) - /* * Curl_get_line() returns only complete whole lines that end with newline. * When 'eof' is set TRUE, the last line has been read. @@ -41,7 +39,7 @@ CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof) curlx_dyn_reset(buf); while(1) { size_t rlen; - char *b = fgets(buffer, sizeof(buffer), input); + const char *b = fgets(buffer, sizeof(buffer), input); *eof = feof(input); @@ -60,7 +58,7 @@ CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof) return CURLE_OK; /* all good */ if(*eof) /* append a newline */ - return appendnl(buf); + return curlx_dyn_addn(buf, "\n", 1); /* otherwise get next line to append */ } /* UNREACHABLE */ diff --git a/vendor/curl/lib/curl_gssapi.c b/vendor/curl/lib/curl_gssapi.c index 8c82f77..c0eb1c4 100644 --- a/vendor/curl/lib/curl_gssapi.c +++ b/vendor/curl/lib/curl_gssapi.c @@ -384,6 +384,7 @@ OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, return gss_delete_sec_context(min, context, output_token); } +#ifdef CURLVERBOSE #define GSS_LOG_BUFFER_LEN 1024 static size_t display_gss_error(OM_uint32 status, int type, char *buf, size_t len) @@ -437,11 +438,8 @@ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, display_gss_error(minor, GSS_C_MECH_CODE, buf, len); infof(data, "%s%s", prefix, buf); -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; - (void)prefix; -#endif } +#endif /* CURLVERBOSE */ #if defined(__GNUC__) && defined(__APPLE__) #pragma GCC diagnostic pop diff --git a/vendor/curl/lib/curl_gssapi.h b/vendor/curl/lib/curl_gssapi.h index 8c09c72..0d36097 100644 --- a/vendor/curl/lib/curl_gssapi.h +++ b/vendor/curl/lib/curl_gssapi.h @@ -47,9 +47,19 @@ OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, gss_ctx_id_t *context_handle, gss_buffer_t output_token); +#ifdef CURLVERBOSE /* Helper to log a GSS-API error status */ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, OM_uint32 major, OM_uint32 minor); +#else +#define Curl_gss_log_error(data, prefix, major, minor) \ + do { \ + (void)(data); \ + (void)(prefix); \ + (void)(major); \ + (void)(minor); \ + } while(0) +#endif /* Define our privacy and integrity protection values */ #define GSSAUTH_P_NONE 1 diff --git a/vendor/curl/lib/curl_hmac.h b/vendor/curl/lib/curl_hmac.h index 4759ff6..b9f218f 100644 --- a/vendor/curl/lib/curl_hmac.h +++ b/vendor/curl/lib/curl_hmac.h @@ -57,10 +57,10 @@ struct HMAC_context { struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams, const unsigned char *key, unsigned int keylen); -int Curl_HMAC_update(struct HMAC_context *context, +int Curl_HMAC_update(struct HMAC_context *ctxt, const unsigned char *data, unsigned int len); -int Curl_HMAC_final(struct HMAC_context *context, unsigned char *result); +int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output); CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, diff --git a/vendor/curl/lib/curl_ldap.h b/vendor/curl/lib/curl_ldap.h index 5ef32a5..7c1e1c4 100644 --- a/vendor/curl/lib/curl_ldap.h +++ b/vendor/curl/lib/curl_ldap.h @@ -23,15 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef CURL_DISABLE_LDAP -extern const struct Curl_handler Curl_handler_ldap; +extern const struct Curl_protocol Curl_protocol_ldap; -#if !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) -extern const struct Curl_handler Curl_handler_ldaps; -#endif +extern const struct Curl_scheme Curl_scheme_ldap; +extern const struct Curl_scheme Curl_scheme_ldaps; void Curl_ldap_version(char *buf, size_t bufsz); -#endif + #endif /* HEADER_CURL_LDAP_H */ diff --git a/vendor/curl/lib/curl_md5.h b/vendor/curl/lib/curl_md5.h index 16272c7..042c5f5 100644 --- a/vendor/curl/lib/curl_md5.h +++ b/vendor/curl/lib/curl_md5.h @@ -58,7 +58,7 @@ CURLcode Curl_md5it(unsigned char *output, const unsigned char *input, struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params); CURLcode Curl_MD5_update(struct MD5_context *context, - const unsigned char *data, + const unsigned char *input, unsigned int len); CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result); diff --git a/vendor/curl/lib/curl_ntlm_core.c b/vendor/curl/lib/curl_ntlm_core.c index 2f4f7c3..4a9433b 100644 --- a/vendor/curl/lib/curl_ntlm_core.c +++ b/vendor/curl/lib/curl_ntlm_core.c @@ -57,11 +57,11 @@ #endif #if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT) - #define USE_OPENSSL_DES +# define USE_OPENSSL_DES #elif defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT) - #define USE_OPENSSL_DES +# define USE_OPENSSL_DES #elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) - #define USE_MBEDTLS_DES +# define USE_MBEDTLS_DES #endif #ifdef USE_OPENSSL_DES @@ -84,18 +84,14 @@ # define DES_set_key_unchecked wolfSSL_DES_set_key_unchecked # define DES_ecb_encrypt wolfSSL_DES_ecb_encrypt # define DESKEY(x) ((WOLFSSL_DES_key_schedule *)(x)) - -# if defined(LIBWOLFSSL_VERSION_HEX) && \ - (LIBWOLFSSL_VERSION_HEX >= 0x05007006) +# if defined(LIBWOLFSSL_VERSION_HEX) && LIBWOLFSSL_VERSION_HEX >= 0x05007006 # define DES_ENCRYPT WC_DES_ENCRYPT # define DES_DECRYPT WC_DES_DECRYPT # endif - # else # define DESKEY(x) &x # endif #endif -#define DESKEYARG(x) *x #elif defined(USE_GNUTLS) @@ -141,7 +137,7 @@ * * bytes [in/out] - The data whose parity bits are to be adjusted for * odd parity. - * len [out] - The length of the data. + * len [in] - The length of the data. */ static void curl_des_set_odd_parity(unsigned char *bytes, size_t len) { @@ -182,8 +178,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key) * Turns a 56-bit key into a 64-bit, odd parity key and sets the key. The * key schedule ks is also set. */ -static void setup_des_key(const unsigned char *key_56, - DES_key_schedule DESKEYARG(ks)) +static void setup_des_key(const unsigned char *key_56, DES_key_schedule *ks) { DES_cblock key; @@ -307,7 +302,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, return TRUE; } -#endif /* USE_WIN32_CRYPTO */ +#endif /* crypto backends */ /* * takes a 21 byte array and treats it as 3 56-bit DES keys. The @@ -340,7 +335,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, des_encrypt(&des, 8, results + 8, plaintext); setup_des_key(keys + 14, &des); des_encrypt(&des, 8, results + 16, plaintext); -#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \ +#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \ defined(USE_WIN32_CRYPTO) encrypt_des(plaintext, results, keys); encrypt_des(plaintext, results + 8, keys + 7); @@ -386,7 +381,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, des_encrypt(&des, 8, lmbuffer, magic); setup_des_key(pw + 7, &des); des_encrypt(&des, 8, lmbuffer + 8, magic); -#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \ +#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \ defined(USE_WIN32_CRYPTO) encrypt_des(magic, lmbuffer, pw); encrypt_des(magic, lmbuffer + 8, pw + 7); @@ -404,24 +399,10 @@ static void ascii_to_unicode_le(unsigned char *dest, const char *src, size_t i; for(i = 0; i < srclen; i++) { dest[2 * i] = (unsigned char)src[i]; - dest[2 * i + 1] = '\0'; - } -} - -#ifndef USE_WINDOWS_SSPI - -static void ascii_uppercase_to_unicode_le(unsigned char *dest, - const char *src, size_t srclen) -{ - size_t i; - for(i = 0; i < srclen; i++) { - dest[2 * i] = (unsigned char)(Curl_raw_toupper(src[i])); - dest[2 * i + 1] = '\0'; + dest[(2 * i) + 1] = '\0'; } } -#endif /* !USE_WINDOWS_SSPI */ - /* * Set up nt hashed passwords * @unittest: 1600 @@ -505,6 +486,16 @@ static void time2filetime(struct ms_filetime *ft, time_t t) #endif } +static void ascii_uppercase_to_unicode_le(unsigned char *dest, + const char *src, size_t srclen) +{ + size_t i; + for(i = 0; i < srclen; i++) { + dest[2 * i] = (unsigned char)(Curl_raw_toupper(src[i])); + dest[(2 * i) + 1] = '\0'; + } +} + /* This creates the NTLMv2 hash by using NTLM hash as the key and Unicode * (uppercase UserName + Domain) as the data */ @@ -554,9 +545,9 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, * * Returns CURLE_OK on success. */ -CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, - unsigned char *challenge_client, - struct ntlmdata *ntlm, +CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash, + const unsigned char *challenge_client, + const struct ntlmdata *ntlm, unsigned char **ntresp, unsigned int *ntresp_len) { @@ -647,9 +638,9 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, * * Returns CURLE_OK on success. */ -CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, - unsigned char *challenge_client, - unsigned char *challenge_server, +CURLcode Curl_ntlm_core_mk_lmv2_resp(const unsigned char *ntlmv2hash, + const unsigned char *challenge_client, + const unsigned char *challenge_server, unsigned char *lmresp) { unsigned char data[16]; diff --git a/vendor/curl/lib/curl_ntlm_core.h b/vendor/curl/lib/curl_ntlm_core.h index 947b836..f96bf0a 100644 --- a/vendor/curl/lib/curl_ntlm_core.h +++ b/vendor/curl/lib/curl_ntlm_core.h @@ -55,15 +55,15 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, unsigned char *ntlmhash, unsigned char *ntlmv2hash); -CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, - unsigned char *challenge_client, - struct ntlmdata *ntlm, +CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash, + const unsigned char *challenge_client, + const struct ntlmdata *ntlm, unsigned char **ntresp, unsigned int *ntresp_len); -CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, - unsigned char *challenge_client, - unsigned char *challenge_server, +CURLcode Curl_ntlm_core_mk_lmv2_resp(const unsigned char *ntlmv2hash, + const unsigned char *challenge_client, + const unsigned char *challenge_server, unsigned char *lmresp); #endif /* !USE_WINDOWS_SSPI */ diff --git a/vendor/curl/lib/curl_printf.h b/vendor/curl/lib/curl_printf.h index 207ba71..3b1a5af 100644 --- a/vendor/curl/lib/curl_printf.h +++ b/vendor/curl/lib/curl_printf.h @@ -28,10 +28,10 @@ #define MERR_MEM 1 #define MERR_TOO_LARGE 2 -/* Lower-case digits. */ +/* Lower-case digits. */ extern const unsigned char Curl_ldigits[]; -/* Upper-case digits. */ +/* Upper-case digits. */ extern const unsigned char Curl_udigits[]; #endif /* HEADER_CURL_PRINTF_H */ diff --git a/vendor/curl/lib/curl_range.c b/vendor/curl/lib/curl_range.c index 14218f1..9bbafa4 100644 --- a/vendor/curl/lib/curl_range.c +++ b/vendor/curl/lib/curl_range.c @@ -30,10 +30,8 @@ /* Only include this function if one or more of FTP, FILE are enabled. */ #if !defined(CURL_DISABLE_FTP) || !defined(CURL_DISABLE_FILE) -/* - Check if this is a range download, and if so, set the internal variables - properly. -*/ +/* Check if this is a range download, and if so, set the internal variables + properly. */ CURLcode Curl_range(struct Curl_easy *data) { if(data->state.use_range && data->state.range) { @@ -56,7 +54,7 @@ CURLcode Curl_range(struct Curl_easy *data) else if(!first_num) { /* -Y */ if(!to) - /* "-0" is just wrong */ + /* "-0" is wrong */ return CURLE_RANGE_ERROR; data->req.maxdownload = to; diff --git a/vendor/curl/lib/curl_rtmp.c b/vendor/curl/lib/curl_rtmp.c index 98816f8..75b8091 100644 --- a/vendor/curl/lib/curl_rtmp.c +++ b/vendor/curl/lib/curl_rtmp.c @@ -24,10 +24,11 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef USE_LIBRTMP - #include "curl_rtmp.h" #include "urldata.h" + +#ifdef USE_LIBRTMP + #include "url.h" #include "curlx/nonblock.h" #include "progress.h" /* for Curl_pgrsSetUploadSize */ @@ -47,172 +48,9 @@ /* meta key for storing RTMP* at connection */ #define CURL_META_RTMP_CONN "meta:proto:rtmp:conn" - -static CURLcode rtmp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode rtmp_do(struct Curl_easy *data, bool *done); -static CURLcode rtmp_done(struct Curl_easy *data, CURLcode, bool premature); -static CURLcode rtmp_connect(struct Curl_easy *data, bool *done); -static CURLcode rtmp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); - static Curl_recv rtmp_recv; static Curl_send rtmp_send; -/* - * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu/ - */ - -const struct Curl_handler Curl_handler_rtmp = { - "rtmp", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMP, /* defport */ - CURLPROTO_RTMP, /* protocol */ - CURLPROTO_RTMP, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmpt = { - "rtmpt", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMPT, /* defport */ - CURLPROTO_RTMPT, /* protocol */ - CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmpe = { - "rtmpe", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMP, /* defport */ - CURLPROTO_RTMPE, /* protocol */ - CURLPROTO_RTMPE, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmpte = { - "rtmpte", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMPT, /* defport */ - CURLPROTO_RTMPTE, /* protocol */ - CURLPROTO_RTMPTE, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmps = { - "rtmps", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMPS, /* defport */ - CURLPROTO_RTMPS, /* protocol */ - CURLPROTO_RTMP, /* family */ - PROTOPT_NONE /* flags */ -}; - -const struct Curl_handler Curl_handler_rtmpts = { - "rtmpts", /* scheme */ - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_RTMPS, /* defport */ - CURLPROTO_RTMPTS, /* protocol */ - CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE /* flags */ -}; - static void rtmp_conn_dtor(void *key, size_t klen, void *entry) { RTMP *r = entry; @@ -383,4 +221,106 @@ void Curl_rtmp_version(char *version, size_t len) suff); } +/* + * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu/ + */ + +static const struct Curl_protocol Curl_protocol_rtmp = { + rtmp_setup_connection, /* setup_connection */ + rtmp_do, /* do_it */ + rtmp_done, /* done */ + ZERO_NULL, /* do_more */ + rtmp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + rtmp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* USE_LIBRTMP */ + +const struct Curl_scheme Curl_scheme_rtmp = { + "rtmp", /* scheme */ +#ifndef USE_LIBRTMP + ZERO_NULL, +#else + &Curl_protocol_rtmp, +#endif + CURLPROTO_RTMP, /* protocol */ + CURLPROTO_RTMP, /* family */ + PROTOPT_NONE, /* flags */ + PORT_RTMP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_rtmpt = { + "rtmpt", /* scheme */ +#ifndef USE_LIBRTMP + ZERO_NULL, +#else + &Curl_protocol_rtmp, +#endif + CURLPROTO_RTMPT, /* protocol */ + CURLPROTO_RTMPT, /* family */ + PROTOPT_NONE, /* flags */ + PORT_RTMPT, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_rtmpe = { + "rtmpe", /* scheme */ +#ifndef USE_LIBRTMP + ZERO_NULL, +#else + &Curl_protocol_rtmp, +#endif + CURLPROTO_RTMPE, /* protocol */ + CURLPROTO_RTMPE, /* family */ + PROTOPT_NONE, /* flags */ + PORT_RTMP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_rtmpte = { + "rtmpte", /* scheme */ +#ifndef USE_LIBRTMP + ZERO_NULL, +#else + &Curl_protocol_rtmp, +#endif + CURLPROTO_RTMPTE, /* protocol */ + CURLPROTO_RTMPTE, /* family */ + PROTOPT_NONE, /* flags */ + PORT_RTMPT, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_rtmps = { + "rtmps", /* scheme */ +#ifndef USE_LIBRTMP + ZERO_NULL, +#else + &Curl_protocol_rtmp, +#endif + CURLPROTO_RTMPS, /* protocol */ + CURLPROTO_RTMP, /* family */ + PROTOPT_NONE, /* flags */ + PORT_RTMPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_rtmpts = { + "rtmpts", /* scheme */ +#ifndef USE_LIBRTMP + ZERO_NULL, +#else + &Curl_protocol_rtmp, +#endif + CURLPROTO_RTMPTS, /* protocol */ + CURLPROTO_RTMPT, /* family */ + PROTOPT_NONE, /* flags */ + PORT_RTMPS, /* defport */ +}; diff --git a/vendor/curl/lib/curl_rtmp.h b/vendor/curl/lib/curl_rtmp.h index 339d3a4..e57d20c 100644 --- a/vendor/curl/lib/curl_rtmp.h +++ b/vendor/curl/lib/curl_rtmp.h @@ -23,14 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +extern const struct Curl_scheme Curl_scheme_rtmp; +extern const struct Curl_scheme Curl_scheme_rtmpt; +extern const struct Curl_scheme Curl_scheme_rtmpe; +extern const struct Curl_scheme Curl_scheme_rtmpte; +extern const struct Curl_scheme Curl_scheme_rtmps; +extern const struct Curl_scheme Curl_scheme_rtmpts; #ifdef USE_LIBRTMP -extern const struct Curl_handler Curl_handler_rtmp; -extern const struct Curl_handler Curl_handler_rtmpt; -extern const struct Curl_handler Curl_handler_rtmpe; -extern const struct Curl_handler Curl_handler_rtmpte; -extern const struct Curl_handler Curl_handler_rtmps; -extern const struct Curl_handler Curl_handler_rtmpts; - void Curl_rtmp_version(char *version, size_t len); #endif diff --git a/vendor/curl/lib/curl_sasl.c b/vendor/curl/lib/curl_sasl.c index 123c1c1..b2eb1fe 100644 --- a/vendor/curl/lib/curl_sasl.c +++ b/vendor/curl/lib/curl_sasl.c @@ -183,9 +183,9 @@ void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, static void sasl_state(struct SASL *sasl, struct Curl_easy *data, saslstate newstate) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* for debug purposes */ - static const char * const names[]={ + static const char * const names[] = { "STOP", "PLAIN", "LOGIN", @@ -227,13 +227,14 @@ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, result = sasl->params->getmessage(data, out); if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) { - unsigned char *msg; - size_t msglen; const char *serverdata = Curl_bufref_ptr(out); if(!*serverdata || *serverdata == '=') Curl_bufref_set(out, NULL, 0, NULL); else { + unsigned char *msg; + size_t msglen; + result = curlx_base64_decode(serverdata, &msg, &msglen); if(!result) Curl_bufref_set(out, msg, msglen, curl_free); @@ -332,8 +333,8 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) Curl_auth_create_gssapi_user_message(data, sctx->conn->user, sctx->conn->passwd, service, sctx->conn->host.name, - sctx->sasl->mutual_auth, NULL, - krb5, &sctx->resp); + (bool)sctx->sasl->mutual_auth, + NULL, krb5, &sctx->resp); } return TRUE; } @@ -711,9 +712,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); result = !krb5 ? CURLE_OUT_OF_MEMORY : Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, - service, conn->host.name, - sasl->mutual_auth, NULL, - krb5, &resp); + service, conn->host.name, + (bool)sasl->mutual_auth, NULL, + krb5, &resp); newstate = SASL_GSSAPI_TOKEN; break; } @@ -728,7 +729,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, message */ result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, NULL, NULL, - sasl->mutual_auth, + (bool)sasl->mutual_auth, &serverdata, krb5, &resp); newstate = SASL_GSSAPI_NO_DATA; @@ -801,7 +802,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, sasl->curmech = NULL; /* Start an alternative SASL authentication */ - return Curl_sasl_start(sasl, data, sasl->force_ir, progress); + return Curl_sasl_start(sasl, data, (bool)sasl->force_ir, progress); default: failf(data, "Unsupported SASL authentication mechanism"); result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ @@ -834,7 +835,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, return result; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static void sasl_unchosen(struct Curl_easy *data, unsigned short mech, unsigned short enabledmechs, bool built_in, bool platform, @@ -865,11 +866,11 @@ static void sasl_unchosen(struct Curl_easy *data, unsigned short mech, infof(data, "SASL: %s is missing username", mname); } } -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE #ifdef USE_KERBEROS5 #define CURL_SASL_KERBEROS5 TRUE #else @@ -924,7 +925,7 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) data->set.str[STRING_BEARER] ? NULL : "CURLOPT_XOAUTH2_BEARER"); } -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ (void)sasl; (void)data; return CURLE_LOGIN_DENIED; diff --git a/vendor/curl/lib/curl_sasl.h b/vendor/curl/lib/curl_sasl.h index 8432d26..8a97f52 100644 --- a/vendor/curl/lib/curl_sasl.h +++ b/vendor/curl/lib/curl_sasl.h @@ -129,7 +129,7 @@ struct SASL { /* This is used to test whether the line starts with the given mechanism */ #define sasl_mech_equal(line, wordlen, mech) \ - (wordlen == (sizeof(mech) - 1) / sizeof(char) && \ + ((wordlen) == (sizeof(mech) - 1) / sizeof(char) && \ !memcmp(line, mech, wordlen)) /* Convert a mechanism name to a token */ @@ -147,11 +147,11 @@ void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, /* Check if we have enough auth data and capabilities to authenticate */ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data); -/* Calculate the required login details for SASL authentication */ +/* Calculate the required login details for SASL authentication */ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, bool force_ir, saslprogress *progress); -/* Continue an SASL authentication */ +/* Continue an SASL authentication */ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, int code, saslprogress *progress); diff --git a/vendor/curl/lib/curl_setup.h b/vendor/curl/lib/curl_setup.h index 05b4aff..4f8d65a 100644 --- a/vendor/curl/lib/curl_setup.h +++ b/vendor/curl/lib/curl_setup.h @@ -89,12 +89,8 @@ #ifdef _MSC_VER /* Disable Visual Studio warnings: 4127 "conditional expression is constant" */ #pragma warning(disable:4127) -/* Avoid VS2005 and upper complaining about portable C functions. */ -#ifndef _CRT_NONSTDC_NO_DEPRECATE /* mingw-w64 v2+. MS SDK ~10+/~VS2017+. */ -#define _CRT_NONSTDC_NO_DEPRECATE /* for close(), fileno(), unlink(), etc. */ -#endif #ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS /* for getenv(), tests: sscanf() */ +#define _CRT_SECURE_NO_WARNINGS /* for getenv(), sscanf() */ #endif #endif /* _MSC_VER */ @@ -110,6 +106,7 @@ # ifndef NOGDI # define NOGDI # endif + /* Detect Windows App environment which has a restricted access * to the Win32 APIs. */ # if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \ @@ -120,6 +117,12 @@ # define CURL_WINDOWS_UWP # endif # endif + +/* Mandatory to define SECURITY_WIN32 or SECURITY_KERNEL to indicating who is + compiling the code. */ +#undef SECURITY_KERNEL +#undef SECURITY_WIN32 +#define SECURITY_WIN32 /* for */ #endif /* Compatibility */ @@ -154,12 +157,20 @@ # include "config-os400.h" #endif -#ifdef __PLAN9__ -# include "config-plan9.h" -#endif - #endif /* HAVE_CONFIG_H */ +#ifdef _WIN32 +# if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600) +# error The minimum build target is Windows Vista (0x0600) +# endif + +# if !defined(CURL_WINDOWS_UWP) && (defined(_MSC_VER) || defined(__MINGW32__)) +# ifndef HAVE_IF_NAMETOINDEX +# define HAVE_IF_NAMETOINDEX +# endif +# endif +#endif + /* ================================================================ */ /* Definition of preprocessor macros/symbols which modify compiler */ /* behavior or generated code characteristics must be done here, */ @@ -259,6 +270,9 @@ # ifndef CURL_DISABLE_TFTP # define CURL_DISABLE_TFTP # endif +# ifndef CURL_DISABLE_WEBSOCKETS +# define CURL_DISABLE_WEBSOCKETS +# endif #endif /* @@ -272,16 +286,39 @@ * When HTTP is disabled, disable HTTP-only features */ #ifdef CURL_DISABLE_HTTP -# define CURL_DISABLE_ALTSVC 1 -# define CURL_DISABLE_COOKIES 1 -# define CURL_DISABLE_BASIC_AUTH 1 -# define CURL_DISABLE_BEARER_AUTH 1 -# define CURL_DISABLE_AWS 1 -# define CURL_DISABLE_DOH 1 -# define CURL_DISABLE_FORM_API 1 -# define CURL_DISABLE_HEADERS_API 1 -# define CURL_DISABLE_HSTS 1 -# define CURL_DISABLE_HTTP_AUTH 1 +# ifndef CURL_DISABLE_ALTSVC +# define CURL_DISABLE_ALTSVC +# endif +# ifndef CURL_DISABLE_COOKIES +# define CURL_DISABLE_COOKIES +# endif +# ifndef CURL_DISABLE_BASIC_AUTH +# define CURL_DISABLE_BASIC_AUTH +# endif +# ifndef CURL_DISABLE_BEARER_AUTH +# define CURL_DISABLE_BEARER_AUTH +# endif +# ifndef CURL_DISABLE_AWS +# define CURL_DISABLE_AWS +# endif +# ifndef CURL_DISABLE_DOH +# define CURL_DISABLE_DOH +# endif +# ifndef CURL_DISABLE_FORM_API +# define CURL_DISABLE_FORM_API +# endif +# ifndef CURL_DISABLE_HEADERS_API +# define CURL_DISABLE_HEADERS_API +# endif +# ifndef CURL_DISABLE_HSTS +# define CURL_DISABLE_HSTS +# endif +# ifndef CURL_DISABLE_HTTP_AUTH +# define CURL_DISABLE_HTTP_AUTH +# endif +# ifndef CURL_DISABLE_WEBSOCKETS +# define CURL_DISABLE_WEBSOCKETS /* no WebSockets without HTTP present */ +# endif #endif /* ================================================================ */ @@ -445,6 +482,10 @@ #define USE_EVENTFD #endif +#ifdef SO_NOSIGPIPE +#define USE_SO_NOSIGPIPE +#endif + #include #include @@ -458,8 +499,19 @@ #include #endif -#if defined(HAVE_STDINT_H) || defined(USE_WOLFSSL) #include +#define HAVE_UINTPTR_T /* assume uintptr_t is provided by stdint.h */ + +#ifdef __DJGPP__ +/* By default, DJGPP provides this type as a version of 'unsigned long' which + forces us to use a define use it in printf() format strings without + warnings. long and int are both 32 bits for this platform. */ +#define uint32_t unsigned int +#endif + +/* Disable uintptr_t for targets known to miss it from stdint.h */ +#ifdef __OS400__ +#undef HAVE_UINTPTR_T #endif #include @@ -471,26 +523,19 @@ # include # include /* Large file (>2Gb) support using Win32 functions. */ -# undef lseek -# define lseek(fdes, offset, whence) _lseeki64(fdes, offset, whence) -# undef fstat -# define fstat(fdes, stp) _fstati64(fdes, stp) -# define struct_stat struct _stati64 -# define LSEEK_ERROR (__int64)-1 +# define curl_lseek _lseeki64 +# define LSEEK_ERROR ((__int64)-1) #elif defined(__DJGPP__) /* Requires DJGPP 2.04 */ # include -# undef lseek -# define lseek(fdes, offset, whence) llseek(fdes, offset, whence) -# define LSEEK_ERROR (offset_t)-1 -#endif - -#ifndef struct_stat -#define struct_stat struct stat -#endif - -#ifndef LSEEK_ERROR -#define LSEEK_ERROR (off_t)-1 +# define curl_lseek llseek +# define LSEEK_ERROR ((offset_t)-1) +#elif defined(__AMIGA__) +# define curl_lseek(fd, offset, whence) lseek(fd, (off_t)(offset), whence) +# define LSEEK_ERROR ((off_t)-1) +#else +# define curl_lseek lseek +# define LSEEK_ERROR ((off_t)-1) #endif #ifndef SIZEOF_TIME_T @@ -500,7 +545,7 @@ #ifndef SIZEOF_CURL_SOCKET_T /* configure and cmake check and set the define */ -# ifdef _WIN64 +# if defined(USE_WINSOCK) && defined(_WIN64) # define SIZEOF_CURL_SOCKET_T 8 # else /* default guess */ @@ -509,12 +554,12 @@ #endif #if SIZEOF_CURL_SOCKET_T < 8 -#ifdef _WIN32 +#ifdef USE_WINSOCK # define FMT_SOCKET_T "u" #else # define FMT_SOCKET_T "d" #endif -#elif defined(_WIN32) +#elif defined(USE_WINSOCK) # define FMT_SOCKET_T "zu" #else # define FMT_SOCKET_T "qd" @@ -663,7 +708,7 @@ #elif defined(USE_ARES) # define CURLRES_ASYNCH # define CURLRES_ARES -/* now undef the stock libc functions just to avoid them being used */ +/* now undef the stock libc functions to avoid them being used */ # undef HAVE_GETADDRINFO # undef HAVE_FREEADDRINFO #else @@ -689,14 +734,9 @@ #endif #if defined(USE_OPENSSL) && defined(USE_WOLFSSL) -# include -# if LIBWOLFSSL_VERSION_HEX >= 0x05007006 -# ifndef OPENSSL_COEXIST -# define OPENSSL_COEXIST -# endif -# else -# error "OpenSSL can only coexist with wolfSSL v5.7.6 or upper" -# endif +#ifndef OPENSSL_COEXIST +#define OPENSSL_COEXIST +#endif #endif #if defined(USE_WOLFSSL) && defined(USE_GNUTLS) @@ -768,27 +808,357 @@ (defined(__clang__) && __clang_major__ >= 10) # define FALLTHROUGH() __attribute__((fallthrough)) #else -# define FALLTHROUGH() do {} while (0) +# define FALLTHROUGH() do {} while(0) +#endif +#endif + +/* + * Inclusion of common header files. + */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include + +#if !defined(_WIN32) || defined(__MINGW32__) +#include +#endif + +#ifdef HAVE_IO_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#if defined(HAVE_STDBOOL_H) && defined(HAVE_BOOL_T) +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +/* Macro to strip 'const' without triggering a compiler warning. + Use it for APIs that do not or cannot support the const qualifier. */ +#ifdef HAVE_UINTPTR_T +#define CURL_UNCONST(p) ((void *)(uintptr_t)(const void *)(p)) +#else +#define CURL_UNCONST(p) ((void *)(p)) /* Fall back to simple cast */ +#endif + +#ifdef USE_SCHANNEL +/* Must set this before is included directly or indirectly by + another Windows header. */ +# define SCHANNEL_USE_BLACKLISTS /* for SCH_CREDENTIALS */ +# include /* for [P]UNICODE_STRING in SCH_CREDENTIALS */ +#endif + +#ifdef __hpux +# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) +# ifdef _APP32_64BIT_OFF_T +# define OLD_APP32_64BIT_OFF_T _APP32_64BIT_OFF_T +# undef _APP32_64BIT_OFF_T +# else +# undef OLD_APP32_64BIT_OFF_T +# endif +# endif +#endif + +#ifndef _WIN32 +#include /* also for MSG_NOSIGNAL */ +#endif + +#include "functypes.h" + +#ifdef __hpux +# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) +# ifdef OLD_APP32_64BIT_OFF_T +# define _APP32_64BIT_OFF_T OLD_APP32_64BIT_OFF_T +# undef OLD_APP32_64BIT_OFF_T +# endif +# endif +#endif + +/* + * Definition of timeval struct for platforms that do not have it. + */ +#ifndef HAVE_STRUCT_TIMEVAL +struct timeval { + long tv_sec; + long tv_usec; +}; +#endif + +/* + * If we have the MSG_NOSIGNAL define, make sure we use + * it as the fourth argument of function send() + */ +#ifdef MSG_NOSIGNAL +#define SEND_4TH_ARG MSG_NOSIGNAL +#else +#define SEND_4TH_ARG 0 #endif + +#ifdef __minix +/* Minix does not support recv on TCP sockets */ +#define sread(x, y, z) (ssize_t)read((RECV_TYPE_ARG1)(x), \ + (RECV_TYPE_ARG2)(y), \ + (RECV_TYPE_ARG3)(z)) + +#elif defined(HAVE_RECV) +/* + * The definitions for the return type and arguments types + * of functions recv() and send() belong and come from the + * configuration file. Do not define them in any other place. + * + * HAVE_RECV is defined if you have a function named recv() + * which is used to read incoming data from sockets. If your + * function has another name then do not define HAVE_RECV. + * + * If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2, + * RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also + * be defined. + * + * HAVE_SEND is defined if you have a function named send() + * which is used to write outgoing data on a connected socket. + * If yours has another name then do not define HAVE_SEND. + * + * If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_TYPE_ARG2, + * SEND_TYPE_ARG3, SEND_TYPE_ARG4 and SEND_TYPE_RETV must also + * be defined. SEND_NONCONST_ARG2 must also be defined if ARG2 + * does not accept const. + */ + +#define sread(x, y, z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \ + (RECV_TYPE_ARG2)(y), \ + (RECV_TYPE_ARG3)(z), \ + (RECV_TYPE_ARG4)(0)) +#else /* HAVE_RECV */ +#ifndef sread +#error "Missing definition of macro sread!" +#endif +#endif /* HAVE_RECV */ + +#ifdef __minix +/* Minix does not support send on TCP sockets */ +#define swrite(x, y, z) (ssize_t)write((SEND_TYPE_ARG1)(x), \ + (SEND_TYPE_ARG2)CURL_UNCONST(y), \ + (SEND_TYPE_ARG3)(z)) +#elif defined(HAVE_SEND) +#ifdef SEND_NONCONST_ARG2 +#define swrite(x, y, z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ + (SEND_TYPE_ARG2)CURL_UNCONST(y), \ + (SEND_TYPE_ARG3)(z), \ + (SEND_TYPE_ARG4)(SEND_4TH_ARG)) +#else +#define swrite(x, y, z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ + (const SEND_TYPE_ARG2)(y), \ + (SEND_TYPE_ARG3)(z), \ + (SEND_TYPE_ARG4)(SEND_4TH_ARG)) +#endif /* SEND_NONCONST_ARG2 */ +#else /* HAVE_SEND */ +#ifndef swrite +#error "Missing definition of macro swrite!" +#endif +#endif /* HAVE_SEND */ + +/* + * Function-like macro definition used to close a socket. + */ +#ifdef HAVE_CLOSESOCKET +# define CURL_SCLOSE(x) closesocket(x) +#elif defined(HAVE_CLOSESOCKET_CAMEL) +# define CURL_SCLOSE(x) CloseSocket(x) +#elif defined(MSDOS) /* Watt-32 */ +# define CURL_SCLOSE(x) close_s(x) +#elif defined(USE_LWIPSOCK) +# define CURL_SCLOSE(x) lwip_close(x) +#else +# define CURL_SCLOSE(x) close(x) #endif /* - * Include macros and defines that should only be processed once. + * Stack-independent version of fcntl() on sockets: */ -#ifndef HEADER_CURL_SETUP_ONCE_H -#include "curl_setup_once.h" +#ifdef USE_LWIPSOCK +# define sfcntl lwip_fcntl +#else +# define sfcntl fcntl #endif +/* + * 'bool' stuff compatible with HP-UX headers. + */ +#if defined(__hpux) && !defined(HAVE_BOOL_T) + typedef int bool; +# define false 0 +# define true 1 +# define HAVE_BOOL_T +#endif + +/* + * 'bool' exists on platforms with , i.e. C99 platforms. + * On non-C99 platforms there is no bool, so define an enum for that. + * On C99 platforms 'false' and 'true' also exist. Enum uses a + * global namespace though, so use bool_false and bool_true. + */ +#ifndef HAVE_BOOL_T + typedef enum { + bool_false = 0, + bool_true = 1 + } bool; + +/* + * Use a define to let 'true' and 'false' use those enums. There + * are currently no use of true and false in libcurl proper, but + * there are some in the examples. This will cater for any later + * code happening to use true and false. + */ +# define false bool_false +# define true bool_true +# define HAVE_BOOL_T +#endif + +/* the type we use for storing a single boolean bit */ +typedef unsigned int curl_bit; +#define BIT(x) curl_bit x:1 + +/* + * Redefine TRUE and FALSE too, to catch current use. With this + * change, 'bool found = 1' will give a warning on MIPSPro, but + * 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro, + * AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too. + */ +#ifndef TRUE +#define TRUE true +#endif +#ifndef FALSE +#define FALSE false +#endif + +#include "curl_ctype.h" + +/* + * Macro used to include code only in debug builds. + */ +#ifdef DEBUGBUILD +#define DEBUGF(x) x +#else +#define DEBUGF(x) do {} while(0) +#endif + +/* + * Macro used to include assertion code only in debug builds. + */ +#undef DEBUGASSERT +#ifdef DEBUGBUILD +#ifdef CURL_DEBUGASSERT +/* External assertion handler for custom integrations */ +#define DEBUGASSERT(x) CURL_DEBUGASSERT(x) +#else +#define DEBUGASSERT(x) assert(x) +#endif +#else +#define DEBUGASSERT(x) do {} while(0) +#endif + +/* + * Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno + * (or equivalent) on this platform to hide platform details to code using it. + */ +#ifdef USE_WINSOCK +#define SOCKERRNO ((int)WSAGetLastError()) +#define SET_SOCKERRNO(x) WSASetLastError((int)(x)) +#else +#define SOCKERRNO errno +#define SET_SOCKERRNO(x) (errno = (x)) +#endif + +/* + * Portable error number symbolic names defined to Winsock error codes. + */ +#ifdef USE_WINSOCK +#define SOCKEACCES WSAEACCES +#define SOCKEADDRINUSE WSAEADDRINUSE +#define SOCKEADDRNOTAVAIL WSAEADDRNOTAVAIL +#define SOCKEAFNOSUPPORT WSAEAFNOSUPPORT +#define SOCKEBADF WSAEBADF +#define SOCKECONNREFUSED WSAECONNREFUSED +#define SOCKECONNRESET WSAECONNRESET +#define SOCKEINPROGRESS WSAEINPROGRESS +#define SOCKEINTR WSAEINTR +#define SOCKEINVAL WSAEINVAL +#define SOCKEISCONN WSAEISCONN +#define SOCKEMSGSIZE WSAEMSGSIZE +/* Use literal value to work around clang-tidy <=20 misreporting + 'readability-uppercase-literal-suffix' with mingw-w64 headers */ +#define SOCKENOMEM 8L /* WSA_NOT_ENOUGH_MEMORY */ +#define SOCKETIMEDOUT WSAETIMEDOUT +#define SOCKEWOULDBLOCK WSAEWOULDBLOCK +#else +#define SOCKEACCES EACCES +#define SOCKEADDRINUSE EADDRINUSE +#define SOCKEADDRNOTAVAIL EADDRNOTAVAIL +#define SOCKEAFNOSUPPORT EAFNOSUPPORT +#define SOCKEBADF EBADF +#define SOCKECONNREFUSED ECONNREFUSED +#define SOCKECONNRESET ECONNRESET +#define SOCKEINPROGRESS EINPROGRESS +#define SOCKEINTR EINTR +#define SOCKEINVAL EINVAL +#define SOCKEISCONN EISCONN +#define SOCKEMSGSIZE EMSGSIZE +#define SOCKENOMEM ENOMEM +#ifdef ETIMEDOUT +#define SOCKETIMEDOUT ETIMEDOUT +#endif +#define SOCKEWOULDBLOCK EWOULDBLOCK +#endif + +/* + * Macro argv_item_t hides platform details to code using it. + */ +#ifdef __VMS +#define argv_item_t __char_ptr32 +#elif defined(_UNICODE) +#define argv_item_t wchar_t * +#else +#define argv_item_t char * +#endif + +/* + * We use this ZERO_NULL to avoid picky compiler warnings, + * when assigning a NULL pointer to a function pointer var. + */ +#define ZERO_NULL 0 + /* * Macros and functions to safely suppress warnings */ #include "curlx/warnless.h" #ifdef _WIN32 -# undef read +# undef read # define read(fd, buf, count) (ssize_t)_read(fd, buf, curlx_uztoui(count)) -# undef write +# undef write # define write(fd, buf, count) (ssize_t)_write(fd, buf, curlx_uztoui(count)) +/* Avoid VS2005+ _CRT_NONSTDC_NO_DEPRECATE warnings about non-portable funcs */ +# undef fileno +# define fileno(fh) _fileno(fh) +# undef unlink +# define unlink(fn) _unlink(fn) +# undef isatty +# define isatty(fd) _isatty(fd) #endif /* @@ -845,12 +1215,23 @@ /* Since O_BINARY is used in bitmasks, setting it to zero makes it usable in source code but yet it does not ruin anything */ -#ifdef O_BINARY +#ifdef _O_BINARY /* for _WIN32 || MSDOS */ +#define CURL_O_BINARY _O_BINARY +#elif defined(O_BINARY) /* __CYGWIN__ */ #define CURL_O_BINARY O_BINARY #else #define CURL_O_BINARY 0 #endif +/* Requires io.h when available */ +#ifdef MSDOS +#define CURL_BINMODE(stream) (void)setmode(fileno(stream), CURL_O_BINARY) +#elif defined(_WIN32) || defined(__CYGWIN__) +#define CURL_BINMODE(stream) (void)_setmode(fileno(stream), CURL_O_BINARY) +#else +#define CURL_BINMODE(stream) (void)(stream) +#endif + /* In Windows the default file mode is text but an application can override it. Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 */ @@ -876,7 +1257,7 @@ endings either CRLF or LF so 't' is appropriate. /* for systems that do not detect this in configure */ #ifndef CURL_SA_FAMILY_T -# ifdef _WIN32 +# ifdef USE_WINSOCK # define CURL_SA_FAMILY_T ADDRESS_FAMILY # elif defined(HAVE_SA_FAMILY_T) # define CURL_SA_FAMILY_T sa_family_t @@ -938,7 +1319,11 @@ extern curl_calloc_callback Curl_ccalloc; #include /* for CURL_EXTERN, curl_socket_t, mprintf.h */ -#ifdef CURLDEBUG +#ifdef DEBUGBUILD +#define CURL_MEMDEBUG +#endif + +#ifdef CURL_MEMDEBUG #ifdef __clang__ # define ALLOC_FUNC __attribute__((__malloc__)) # if __clang_major__ >= 4 @@ -1021,10 +1406,10 @@ CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, #define CURL_FREEADDRINFO(data) \ curl_dbg_freeaddrinfo(data, __LINE__, __FILE__) #define CURL_SOCKET(domain, type, protocol) \ - curl_dbg_socket((int)domain, type, protocol, __LINE__, __FILE__) + curl_dbg_socket((int)(domain), type, protocol, __LINE__, __FILE__) #ifdef HAVE_SOCKETPAIR #define CURL_SOCKETPAIR(domain, type, protocol, socket_vector) \ - curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \ + curl_dbg_socketpair((int)(domain), type, protocol, socket_vector, \ __LINE__, __FILE__) #endif #define CURL_ACCEPT(sock, addr, len) \ @@ -1034,7 +1419,7 @@ CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__) #endif -#else /* !CURLDEBUG */ +#else /* !CURL_MEMDEBUG */ #define sclose(x) CURL_SCLOSE(x) #define fake_sclose(x) Curl_nop_stmt @@ -1050,11 +1435,17 @@ CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, #define CURL_ACCEPT4 accept4 #endif -#endif /* CURLDEBUG */ +#endif /* CURL_MEMDEBUG */ /* Allocator macros */ -#ifdef CURLDEBUG +#ifdef _WIN32 +#define CURLX_STRDUP_LOW _strdup +#else +#define CURLX_STRDUP_LOW strdup +#endif + +#ifdef CURL_MEMDEBUG #define curlx_strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) #define curlx_malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__) @@ -1068,39 +1459,35 @@ CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, #ifdef UNICODE #define curlx_tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) #else -#define curlx_tcsdup(ptr) curlx_strdup(ptr) +#define curlx_tcsdup curlx_strdup #endif #endif /* _WIN32 */ -#else /* !CURLDEBUG */ +#else /* !CURL_MEMDEBUG */ #ifdef BUILDING_LIBCURL -#define curlx_strdup(ptr) Curl_cstrdup(ptr) -#define curlx_malloc(size) Curl_cmalloc(size) -#define curlx_calloc(nbelem, size) Curl_ccalloc(nbelem, size) -#define curlx_realloc(ptr, size) Curl_crealloc(ptr, size) -#define curlx_free(ptr) Curl_cfree(ptr) +#define curlx_strdup Curl_cstrdup +#define curlx_malloc Curl_cmalloc +#define curlx_calloc Curl_ccalloc +#define curlx_realloc Curl_crealloc +#define curlx_free Curl_cfree #else /* !BUILDING_LIBCURL */ -#ifdef _WIN32 -#define curlx_strdup(ptr) _strdup(ptr) -#else -#define curlx_strdup(ptr) strdup(ptr) -#endif -#define curlx_malloc(size) malloc(size) -#define curlx_calloc(nbelem, size) calloc(nbelem, size) -#define curlx_realloc(ptr, size) realloc(ptr, size) -#define curlx_free(ptr) free(ptr) +#define curlx_strdup CURLX_STRDUP_LOW +#define curlx_malloc malloc +#define curlx_calloc calloc +#define curlx_realloc realloc +#define curlx_free free #endif /* BUILDING_LIBCURL */ #ifdef _WIN32 #ifdef UNICODE -#define curlx_tcsdup(ptr) Curl_wcsdup(ptr) +#define curlx_tcsdup curlx_wcsdup #else -#define curlx_tcsdup(ptr) curlx_strdup(ptr) +#define curlx_tcsdup curlx_strdup #endif #endif /* _WIN32 */ -#endif /* CURLDEBUG */ +#endif /* CURL_MEMDEBUG */ /* Some versions of the Android NDK is missing the declaration */ #if defined(HAVE_GETPWUID_R) && \ @@ -1158,8 +1545,11 @@ typedef struct sockaddr_un { #endif #ifdef USE_OPENSSL -/* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no - replacements (yet) so tell the compiler to not warn for them. */ +/* OpenSSL 3 marks these functions deprecated but we have no replacements (yet) + so tell the compiler to not warn for them: + - DES_* (for NTLM), SSL_CTX_set_srp_* (for TLS-SRP) + - EVP_PKEY_get1_RSA, MD5_*, RSA_flags, RSA_free (auto-skipped for OpenSSL + built with no-deprecated) */ # define OPENSSL_SUPPRESS_DEPRECATED # ifdef _WIN32 /* Silence LibreSSL warnings about wincrypt.h collision. Works in 3.8.2+ */ @@ -1191,4 +1581,20 @@ typedef struct sockaddr_un { # define CURL_INLINE /* empty */ #endif +/* Detect if compiler supports C99 variadic macros */ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ + defined(_MSC_VER) +#define CURL_HAVE_MACRO_VARARG +#endif + +#if !defined(CURL_HAVE_MACRO_VARARG) || \ + (defined(CURL_HAVE_MACRO_VARARG) && !defined(CURL_DISABLE_VERBOSE_STRINGS)) +#define CURLVERBOSE +#define VERBOSE(x) x +#define NOVERBOSE(x) Curl_nop_stmt +#else +#define VERBOSE(x) Curl_nop_stmt +#define NOVERBOSE(x) x +#endif + #endif /* HEADER_CURL_SETUP_H */ diff --git a/vendor/curl/lib/curl_setup_once.h b/vendor/curl/lib/curl_setup_once.h deleted file mode 100644 index 06ccc98..0000000 --- a/vendor/curl/lib/curl_setup_once.h +++ /dev/null @@ -1,346 +0,0 @@ -#ifndef HEADER_CURL_SETUP_ONCE_H -#define HEADER_CURL_SETUP_ONCE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -/* - * Inclusion of common header files. - */ - -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -#include - -#if !defined(_WIN32) || defined(__MINGW32__) -#include -#endif - -#ifdef HAVE_IO_H -#include -#endif - -#ifdef HAVE_FCNTL_H -#include -#endif - -#if defined(HAVE_STDBOOL_H) && defined(HAVE_BOOL_T) -#include -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif - -/* Macro to strip 'const' without triggering a compiler warning. - Use it for APIs that do not or cannot support the const qualifier. */ -#ifdef HAVE_STDINT_H -# define CURL_UNCONST(p) ((void *)(uintptr_t)(const void *)(p)) -#else -# define CURL_UNCONST(p) ((void *)(p)) /* Fall back to simple cast */ -#endif - -#ifdef USE_SCHANNEL -/* Must set this before is included directly or indirectly by - another Windows header. */ -# define SCHANNEL_USE_BLACKLISTS 1 -#endif - -#ifdef __hpux -# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) -# ifdef _APP32_64BIT_OFF_T -# define OLD_APP32_64BIT_OFF_T _APP32_64BIT_OFF_T -# undef _APP32_64BIT_OFF_T -# else -# undef OLD_APP32_64BIT_OFF_T -# endif -# endif -#endif - -#ifndef _WIN32 -#include -#endif - -#include "functypes.h" - -#ifdef __hpux -# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) -# ifdef OLD_APP32_64BIT_OFF_T -# define _APP32_64BIT_OFF_T OLD_APP32_64BIT_OFF_T -# undef OLD_APP32_64BIT_OFF_T -# endif -# endif -#endif - -/* - * Definition of timeval struct for platforms that do not have it. - */ -#ifndef HAVE_STRUCT_TIMEVAL -struct timeval { - long tv_sec; - long tv_usec; -}; -#endif - -/* - * If we have the MSG_NOSIGNAL define, make sure we use - * it as the fourth argument of function send() - */ -#ifdef HAVE_MSG_NOSIGNAL -#define SEND_4TH_ARG MSG_NOSIGNAL -#else -#define SEND_4TH_ARG 0 -#endif - -#ifdef __minix -/* Minix does not support recv on TCP sockets */ -#define sread(x, y, z) (ssize_t)read((RECV_TYPE_ARG1)(x), \ - (RECV_TYPE_ARG2)(y), \ - (RECV_TYPE_ARG3)(z)) - -#elif defined(HAVE_RECV) -/* - * The definitions for the return type and arguments types - * of functions recv() and send() belong and come from the - * configuration file. Do not define them in any other place. - * - * HAVE_RECV is defined if you have a function named recv() - * which is used to read incoming data from sockets. If your - * function has another name then do not define HAVE_RECV. - * - * If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2, - * RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also - * be defined. - * - * HAVE_SEND is defined if you have a function named send() - * which is used to write outgoing data on a connected socket. - * If yours has another name then do not define HAVE_SEND. - * - * If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_QUAL_ARG2, - * SEND_TYPE_ARG2, SEND_TYPE_ARG3, SEND_TYPE_ARG4 and - * SEND_TYPE_RETV must also be defined. - */ - -#define sread(x, y, z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \ - (RECV_TYPE_ARG2)(y), \ - (RECV_TYPE_ARG3)(z), \ - (RECV_TYPE_ARG4)(0)) -#else /* HAVE_RECV */ -#ifndef sread -#error "Missing definition of macro sread!" -#endif -#endif /* HAVE_RECV */ - -#ifdef __minix -/* Minix does not support send on TCP sockets */ -#define swrite(x, y, z) (ssize_t)write((SEND_TYPE_ARG1)(x), \ - (SEND_TYPE_ARG2)CURL_UNCONST(y), \ - (SEND_TYPE_ARG3)(z)) -#elif defined(HAVE_SEND) -#define swrite(x, y, z) (ssize_t)send((SEND_TYPE_ARG1)(x), \ - (SEND_QUAL_ARG2 SEND_TYPE_ARG2)CURL_UNCONST(y), \ - (SEND_TYPE_ARG3)(z), \ - (SEND_TYPE_ARG4)(SEND_4TH_ARG)) -#else /* HAVE_SEND */ -#ifndef swrite -#error "Missing definition of macro swrite!" -#endif -#endif /* HAVE_SEND */ - -/* - * Function-like macro definition used to close a socket. - */ -#ifdef HAVE_CLOSESOCKET -# define CURL_SCLOSE(x) closesocket(x) -#elif defined(HAVE_CLOSESOCKET_CAMEL) -# define CURL_SCLOSE(x) CloseSocket(x) -#elif defined(MSDOS) /* Watt-32 */ -# define CURL_SCLOSE(x) close_s(x) -#elif defined(USE_LWIPSOCK) -# define CURL_SCLOSE(x) lwip_close(x) -#else -# define CURL_SCLOSE(x) close(x) -#endif - -/* - * Stack-independent version of fcntl() on sockets: - */ -#ifdef USE_LWIPSOCK -# define sfcntl lwip_fcntl -#else -# define sfcntl fcntl -#endif - -/* - * 'bool' stuff compatible with HP-UX headers. - */ -#if defined(__hpux) && !defined(HAVE_BOOL_T) - typedef int bool; -# define false 0 -# define true 1 -# define HAVE_BOOL_T -#endif - -/* - * 'bool' exists on platforms with , i.e. C99 platforms. - * On non-C99 platforms there is no bool, so define an enum for that. - * On C99 platforms 'false' and 'true' also exist. Enum uses a - * global namespace though, so use bool_false and bool_true. - */ -#ifndef HAVE_BOOL_T - typedef enum { - bool_false = 0, - bool_true = 1 - } bool; - -/* - * Use a define to let 'true' and 'false' use those enums. There - * are currently no use of true and false in libcurl proper, but - * there are some in the examples. This will cater for any later - * code happening to use true and false. - */ -# define false bool_false -# define true bool_true -# define HAVE_BOOL_T -#endif - -/* the type we use for storing a single boolean bit */ -#ifdef _MSC_VER -typedef bool bit; -#define BIT(x) bool x -#else -typedef unsigned int bit; -#define BIT(x) bit x:1 -#endif - -/* - * Redefine TRUE and FALSE too, to catch current use. With this - * change, 'bool found = 1' will give a warning on MIPSPro, but - * 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro, - * AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too. - */ -#ifndef TRUE -#define TRUE true -#endif -#ifndef FALSE -#define FALSE false -#endif - -#include "curl_ctype.h" - -/* - * Macro used to include code only in debug builds. - */ -#ifdef DEBUGBUILD -#define DEBUGF(x) x -#else -#define DEBUGF(x) do {} while(0) -#endif - -/* - * Macro used to include assertion code only in debug builds. - */ -#undef DEBUGASSERT -#ifdef DEBUGBUILD -#define DEBUGASSERT(x) assert(x) -#else -#define DEBUGASSERT(x) do {} while(0) -#endif - -/* - * Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno - * (or equivalent) on this platform to hide platform details to code using it. - */ -#ifdef USE_WINSOCK -#define SOCKERRNO ((int)WSAGetLastError()) -#define SET_SOCKERRNO(x) (WSASetLastError((int)(x))) -#else -#define SOCKERRNO (errno) -#define SET_SOCKERRNO(x) (errno = (x)) -#endif - -/* - * Portable error number symbolic names defined to Winsock error codes. - */ -#ifdef USE_WINSOCK -#define SOCKEACCES WSAEACCES -#define SOCKEADDRINUSE WSAEADDRINUSE -#define SOCKEADDRNOTAVAIL WSAEADDRNOTAVAIL -#define SOCKEAFNOSUPPORT WSAEAFNOSUPPORT -#define SOCKEBADF WSAEBADF -#define SOCKECONNREFUSED WSAECONNREFUSED -#define SOCKECONNRESET WSAECONNRESET -#define SOCKEINPROGRESS WSAEINPROGRESS -#define SOCKEINTR WSAEINTR -#define SOCKEINVAL WSAEINVAL -#define SOCKEISCONN WSAEISCONN -#define SOCKEMSGSIZE WSAEMSGSIZE -#define SOCKENOMEM WSA_NOT_ENOUGH_MEMORY -#define SOCKETIMEDOUT WSAETIMEDOUT -#define SOCKEWOULDBLOCK WSAEWOULDBLOCK -#else -#define SOCKEACCES EACCES -#define SOCKEADDRINUSE EADDRINUSE -#define SOCKEADDRNOTAVAIL EADDRNOTAVAIL -#define SOCKEAFNOSUPPORT EAFNOSUPPORT -#define SOCKEBADF EBADF -#define SOCKECONNREFUSED ECONNREFUSED -#define SOCKECONNRESET ECONNRESET -#define SOCKEINPROGRESS EINPROGRESS -#define SOCKEINTR EINTR -#define SOCKEINVAL EINVAL -#define SOCKEISCONN EISCONN -#define SOCKEMSGSIZE EMSGSIZE -#define SOCKENOMEM ENOMEM -#ifdef ETIMEDOUT -#define SOCKETIMEDOUT ETIMEDOUT -#endif -#define SOCKEWOULDBLOCK EWOULDBLOCK -#endif - -/* - * Macro argv_item_t hides platform details to code using it. - */ -#ifdef __VMS -#define argv_item_t __char_ptr32 -#elif defined(_UNICODE) -#define argv_item_t wchar_t * -#else -#define argv_item_t char * -#endif - -/* - * We use this ZERO_NULL to avoid picky compiler warnings, - * when assigning a NULL pointer to a function pointer var. - */ -#define ZERO_NULL 0 - -#endif /* HEADER_CURL_SETUP_ONCE_H */ diff --git a/vendor/curl/lib/curl_sha256.h b/vendor/curl/lib/curl_sha256.h index 0aaa412..bc1512e 100644 --- a/vendor/curl/lib/curl_sha256.h +++ b/vendor/curl/lib/curl_sha256.h @@ -37,7 +37,7 @@ extern const struct HMAC_params Curl_HMAC_SHA256; #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ #endif -CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input, +CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, const size_t len); #endif diff --git a/vendor/curl/lib/curl_sha512_256.c b/vendor/curl/lib/curl_sha512_256.c index 44ba9be..648e3e3 100644 --- a/vendor/curl/lib/curl_sha512_256.c +++ b/vendor/curl/lib/curl_sha512_256.c @@ -53,7 +53,7 @@ * The bug was fixed in NetBSD 9.4 release, NetBSD 10.0 release, * NetBSD 10.99.11 development. * It is safe to apply the workaround even if the bug is not present, as - * the workaround just reduces performance slightly. */ + * the workaround reduces performance slightly. */ # include # if __NetBSD_Version__ < 904000000 || \ (__NetBSD_Version__ >= 999000000 && \ @@ -147,7 +147,7 @@ static CURLcode Curl_sha512_256_update(void *context, * * @param context the calculation context * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE - # bytes + * bytes * @return CURLE_OK if succeed, * error code otherwise */ @@ -229,7 +229,7 @@ static CURLcode Curl_sha512_256_update(void *context, * * @param context the calculation context * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE - # bytes + * bytes * @return always CURLE_OK */ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) @@ -452,14 +452,14 @@ static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS], /* Four 'Sigma' macro functions. See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ -#define SIG0(x) \ - (Curl_rotr64((x), 28) ^ Curl_rotr64((x), 34) ^ Curl_rotr64((x), 39)) -#define SIG1(x) \ - (Curl_rotr64((x), 14) ^ Curl_rotr64((x), 18) ^ Curl_rotr64((x), 41)) -#define sig0(x) \ - (Curl_rotr64((x), 1) ^ Curl_rotr64((x), 8) ^ ((x) >> 7)) -#define sig1(x) \ - (Curl_rotr64((x), 19) ^ Curl_rotr64((x), 61) ^ ((x) >> 6)) +#define SIG0(x) \ + (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39)) +#define SIG1(x) \ + (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41)) +#define sig0(x) \ + (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7)) +#define sig1(x) \ + (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6)) if(1) { unsigned int t; @@ -520,8 +520,8 @@ static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS], used. */ #define SHA2STEP64(vA, vB, vC, vD, vE, vF, vG, vH, kt, wt) \ do { \ - (vD) += ((vH) += SIG1((vE)) + Sha512_Ch((vE), (vF), (vG)) + (kt) + (wt)); \ - (vH) += SIG0((vA)) + Sha512_Maj((vA), (vB), (vC)); \ + (vD) += ((vH) += SIG1(vE) + Sha512_Ch(vE, vF, vG) + (kt) + (wt)); \ + (vH) += SIG0(vA) + Sha512_Maj(vA, vB, vC); \ } while(0) /* One step of SHA-512/256 computation with working variables rotation, @@ -530,7 +530,7 @@ static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS], #define SHA2STEP64RV(vA, vB, vC, vD, vE, vF, vG, vH, kt, wt) \ do { \ uint64_t tmp_h_ = (vH); \ - SHA2STEP64((vA), (vB), (vC), (vD), (vE), (vF), (vG), tmp_h_, (kt), (wt)); \ + SHA2STEP64(vA, vB, vC, vD, vE, vF, vG, tmp_h_, kt, wt); \ (vH) = (vG); \ (vG) = (vF); \ (vF) = (vE); \ @@ -546,7 +546,7 @@ static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS], Input data must be read in big-endian bytes order, see FIPS PUB 180-4 section 3.1.2. */ #define SHA512_GET_W_FROM_DATA(buf, t) \ - CURL_GET_64BIT_BE(((const uint8_t *)(buf)) + (t) * SHA512_256_BYTES_IN_WORD) + CURL_GET_64BIT_BE((const uint8_t *)(buf) + ((t) * SHA512_256_BYTES_IN_WORD)) /* During first 16 steps, before making any calculation on each step, the W element is read from the input data buffer as a big-endian value and @@ -663,7 +663,7 @@ static CURLcode Curl_sha512_256_update(void *context, * * @param context the calculation context * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE - # bytes + * bytes * @return always CURLE_OK */ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) @@ -727,10 +727,10 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) /* Put in BE mode the leftmost part of the hash as the final digest. See FIPS PUB 180-4 section 6.7. */ - CURL_PUT_64BIT_BE(digest + 0 * SHA512_256_BYTES_IN_WORD, ctx->H[0]); - CURL_PUT_64BIT_BE(digest + 1 * SHA512_256_BYTES_IN_WORD, ctx->H[1]); - CURL_PUT_64BIT_BE(digest + 2 * SHA512_256_BYTES_IN_WORD, ctx->H[2]); - CURL_PUT_64BIT_BE(digest + 3 * SHA512_256_BYTES_IN_WORD, ctx->H[3]); + CURL_PUT_64BIT_BE(digest + (0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]); + CURL_PUT_64BIT_BE(digest + (1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]); + CURL_PUT_64BIT_BE(digest + (2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]); + CURL_PUT_64BIT_BE(digest + (3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]); /* Erase potentially sensitive data. */ memset(ctx, 0, sizeof(struct Curl_sha512_256ctx)); diff --git a/vendor/curl/lib/curl_share.c b/vendor/curl/lib/curl_share.c index 3ff2755..338727a 100644 --- a/vendor/curl/lib/curl_share.c +++ b/vendor/curl/lib/curl_share.c @@ -24,7 +24,6 @@ #include "curl_setup.h" #include "urldata.h" -#include "connect.h" #include "curl_share.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" diff --git a/vendor/curl/lib/curl_share.h b/vendor/curl/lib/curl_share.h index fcd9e32..5e3f82a 100644 --- a/vendor/curl/lib/curl_share.h +++ b/vendor/curl/lib/curl_share.h @@ -36,8 +36,8 @@ struct Curl_ssl_scache; #define CURL_GOOD_SHARE 0x7e117a1e #define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE) -#define CURL_SHARE_KEEP_CONNECT(s) \ - ((s) && ((s)->specifier & (1 << CURL_LOCK_DATA_CONNECT))) +#define CURL_SHARE_KEEP_CONNECT(s) \ + ((s) && ((s)->specifier & (1 << CURL_LOCK_DATA_CONNECT))) /* this struct is libcurl-private, do not export details */ struct Curl_share { @@ -65,13 +65,13 @@ struct Curl_share { #endif }; -CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data, - curl_lock_access); -CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data); +CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, + curl_lock_access accesstype); +CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type); /* convenience macro to check if this handle is using a shared SSL spool */ -#define CURL_SHARE_ssl_scache(data) (data->share && \ - (data->share->specifier & \ +#define CURL_SHARE_ssl_scache(data) ((data)->share && \ + ((data)->share->specifier & \ (1 << CURL_LOCK_DATA_SSL_SESSION))) #endif /* HEADER_CURL_SHARE_H */ diff --git a/vendor/curl/lib/curl_sspi.c b/vendor/curl/lib/curl_sspi.c index 6e27269..1d4cf92 100644 --- a/vendor/curl/lib/curl_sspi.c +++ b/vendor/curl/lib/curl_sspi.c @@ -26,7 +26,7 @@ #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "curlx/multibyte.h" /* Pointer to SSPI dispatch table */ diff --git a/vendor/curl/lib/curl_sspi.h b/vendor/curl/lib/curl_sspi.h index 0fba510..3779d51 100644 --- a/vendor/curl/lib/curl_sspi.h +++ b/vendor/curl/lib/curl_sspi.h @@ -27,17 +27,7 @@ #ifdef USE_WINDOWS_SSPI -/* - * When including the following three headers, it is mandatory to define either - * SECURITY_WIN32 or SECURITY_KERNEL, indicating who is compiling the code. - */ - -#undef SECURITY_WIN32 -#undef SECURITY_KERNEL -#define SECURITY_WIN32 1 -#include #include -#include CURLcode Curl_sspi_global_init(void); void Curl_sspi_global_cleanup(void); @@ -56,7 +46,7 @@ void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity); /* Forward-declaration of global variables defined in curl_sspi.c */ extern PSecurityFunctionTable Curl_pSecFn; -/* Provide some definitions missing in old headers */ +/* Provide Service Principal names as macros */ #define SP_NAME_DIGEST "WDigest" #define SP_NAME_NTLM "NTLM" #define SP_NAME_NEGOTIATE "Negotiate" diff --git a/vendor/curl/lib/curl_threads.h b/vendor/curl/lib/curl_threads.h index 241014c..0e067c0 100644 --- a/vendor/curl/lib/curl_threads.h +++ b/vendor/curl/lib/curl_threads.h @@ -41,11 +41,7 @@ # define curl_mutex_t CRITICAL_SECTION # define curl_thread_t HANDLE # define curl_thread_t_null (HANDLE)0 -# if !defined(_WIN32_WINNT) || (_WIN32_WINNT < _WIN32_WINNT_VISTA) -# define Curl_mutex_init(m) InitializeCriticalSection(m) -# else -# define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1) -# endif +# define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1) # define Curl_mutex_acquire(m) EnterCriticalSection(m) # define Curl_mutex_release(m) LeaveCriticalSection(m) # define Curl_mutex_destroy(m) DeleteCriticalSection(m) diff --git a/vendor/curl/lib/curl_trc.c b/vendor/curl/lib/curl_trc.c index 565d1b4..4b8734a 100644 --- a/vendor/curl/lib/curl_trc.c +++ b/vendor/curl/lib/curl_trc.c @@ -194,6 +194,36 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...) } } +void Curl_reset_fail(struct Curl_easy *data) +{ + if(data->set.errorbuffer) + data->set.errorbuffer[0] = 0; + data->state.errorbuf = FALSE; +} + +#ifdef CURLVERBOSE +struct curl_trc_feat Curl_trc_feat_multi = { + "MULTI", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_read = { + "READ", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_write = { + "WRITE", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_dns = { + "DNS", + CURL_LOG_LVL_NONE, +}; +struct curl_trc_feat Curl_trc_feat_timer = { + "TIMER", + CURL_LOG_LVL_NONE, +}; +#endif + #ifndef CURL_DISABLE_VERBOSE_STRINGS static void trc_infof(struct Curl_easy *data, @@ -248,27 +278,6 @@ void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf, } } -struct curl_trc_feat Curl_trc_feat_multi = { - "MULTI", - CURL_LOG_LVL_NONE, -}; -struct curl_trc_feat Curl_trc_feat_read = { - "READ", - CURL_LOG_LVL_NONE, -}; -struct curl_trc_feat Curl_trc_feat_write = { - "WRITE", - CURL_LOG_LVL_NONE, -}; -struct curl_trc_feat Curl_trc_feat_dns = { - "DNS", - CURL_LOG_LVL_NONE, -}; -struct curl_trc_feat Curl_trc_feat_timer = { - "TIMER", - CURL_LOG_LVL_NONE, -}; - static const char * const Curl_trc_timer_names[] = { "100_TIMEOUT", "ASYNC_NAME", @@ -485,7 +494,7 @@ void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...) } #endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */ -#define TRC_CT_NONE (0) +#define TRC_CT_NONE 0 #define TRC_CT_PROTOCOL (1 << 0) #define TRC_CT_NETWORK (1 << 1) #define TRC_CT_PROXY (1 << 2) @@ -578,7 +587,7 @@ static void trc_apply_level_by_name(struct Curl_str *token, int lvl) } } -static void trc_apply_level_by_category(int category, int lvl) +static void trc_apply_level_by_category(unsigned int category, int lvl) { size_t i; diff --git a/vendor/curl/lib/curl_trc.h b/vendor/curl/lib/curl_trc.h index ff085d6..a82c945 100644 --- a/vendor/curl/lib/curl_trc.h +++ b/vendor/curl/lib/curl_trc.h @@ -61,13 +61,14 @@ void Curl_failf(struct Curl_easy *data, #define failf Curl_failf +/* In case failf() reported into the errorbuf, clear it again. + * This is used to clear information from happy eyeballing attempts + * when ultimately a successful attempt was made. */ +void Curl_reset_fail(struct Curl_easy *data); + #define CURL_LOG_LVL_NONE 0 #define CURL_LOG_LVL_INFO 1 -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#define CURL_HAVE_C99 -#endif - /** * Output an informational message when transfer's verbose logging is enabled. */ @@ -133,7 +134,7 @@ void Curl_trc_ws(struct Curl_easy *data, #define CURL_TRC_TIMER_is_verbose(data) \ Curl_trc_ft_is_verbose(data, &Curl_trc_feat_timer) -#if defined(CURL_HAVE_C99) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(CURL_HAVE_MACRO_VARARG) && !defined(CURL_DISABLE_VERBOSE_STRINGS) #define infof(data, ...) \ do { \ if(Curl_trc_is_verbose(data)) \ @@ -206,7 +207,70 @@ void Curl_trc_ws(struct Curl_easy *data, } while(0) #endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */ -#else /* CURL_HAVE_C99 */ +#elif defined(CURL_HAVE_MACRO_VARARG) && defined(CURL_DISABLE_VERBOSE_STRINGS) + +#define infof(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_M(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_CF(data, cf, ...) \ + do { \ + (void)(data); \ + (void)(cf); \ + } while(0) +#define CURL_TRC_WRITE(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_READ(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_DNS(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#define CURL_TRC_TIMER(data, tid, ...) \ + do { \ + (void)(data); \ + (void)(tid); \ + } while(0) +#ifndef CURL_DISABLE_FTP +#define CURL_TRC_FTP(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif /* !CURL_DISABLE_FTP */ +#ifndef CURL_DISABLE_SMTP +#define CURL_TRC_SMTP(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif /* !CURL_DISABLE_SMTP */ +#ifdef USE_SSL +#define CURL_TRC_SSLS(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif /* USE_SSL */ +#ifdef USE_SSH +#define CURL_TRC_SSH(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif /* USE_SSH */ +#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +#define CURL_TRC_WS(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif + +#else /* !CURL_HAVE_MACRO_VARARG */ #define infof Curl_infof #define CURL_TRC_M Curl_trc_multi @@ -232,17 +296,18 @@ void Curl_trc_ws(struct Curl_easy *data, #define CURL_TRC_WS Curl_trc_ws #endif -#endif /* !CURL_HAVE_C99 */ - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -/* informational messages enabled */ +#endif /* CURL_HAVE_MACRO_VARARG */ +#ifdef CURLVERBOSE extern struct curl_trc_feat Curl_trc_feat_multi; extern struct curl_trc_feat Curl_trc_feat_read; extern struct curl_trc_feat Curl_trc_feat_write; extern struct curl_trc_feat Curl_trc_feat_dns; extern struct curl_trc_feat Curl_trc_feat_timer; +#endif +#ifndef CURL_DISABLE_VERBOSE_STRINGS +/* informational messages enabled */ #define Curl_trc_is_verbose(data) \ ((data) && (data)->set.verbose && \ (!(data)->state.feat || \ @@ -262,13 +327,11 @@ extern struct curl_trc_feat Curl_trc_feat_timer; #else /* CURL_DISABLE_VERBOSE_STRINGS */ /* All informational messages are not compiled in for size savings */ - -#define Curl_trc_is_verbose(d) (FALSE) -#define Curl_trc_cf_is_verbose(x, y) (FALSE) -#define Curl_trc_ft_is_verbose(x, y) (FALSE) +#define Curl_trc_is_verbose(d) FALSE +#define Curl_trc_cf_is_verbose(x, y) FALSE +#define Curl_trc_ft_is_verbose(x, y) FALSE #define CURL_MSTATE_NAME(x) ((void)(x), "-") #define CURL_TRC_EASY_TIMERS(x) Curl_nop_stmt - #endif /* !CURL_DISABLE_VERBOSE_STRINGS */ #endif /* HEADER_CURL_TRC_H */ diff --git a/vendor/curl/lib/curlx/base64.c b/vendor/curl/lib/curlx/base64.c index 0e44eae..a7a6703 100644 --- a/vendor/curl/lib/curlx/base64.c +++ b/vendor/curl/lib/curlx/base64.c @@ -24,9 +24,9 @@ /* Base64 encoding/decoding */ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "base64.h" +#include "curlx/base64.h" /* ---- Base64 Encoding/Decoding Table --- */ const char curlx_base64encdec[] = @@ -182,7 +182,7 @@ static CURLcode base64_encode(const char *table64, if(insize > CURL_MAX_BASE64_INPUT) return CURLE_TOO_LARGE; - base64data = output = curlx_malloc((insize + 2) / 3 * 4 + 1); + base64data = output = curlx_malloc(((insize + 2) / 3 * 4) + 1); if(!output) return CURLE_OUT_OF_MEMORY; diff --git a/vendor/curl/lib/curlx/basename.c b/vendor/curl/lib/curlx/basename.c new file mode 100644 index 0000000..d2fd160 --- /dev/null +++ b/vendor/curl/lib/curlx/basename.c @@ -0,0 +1,74 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef HAVE_BASENAME + +#include "curlx/basename.h" + +/* + (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 + Edition) + + The basename() function shall take the pathname pointed to by path and + return a pointer to the final component of the pathname, deleting any + trailing '/' characters. + + If the string pointed to by path consists entirely of the '/' character, + basename() shall return a pointer to the string "/". If the string pointed + to by path is exactly "//", it is implementation-defined whether '/' or "//" + is returned. + + If path is a null pointer or points to an empty string, basename() shall + return a pointer to the string ".". + + The basename() function may modify the string pointed to by path, and may + return a pointer to static storage that may then be overwritten by a + subsequent call to basename(). + + The basename() function need not be reentrant. A function that is not + required to be reentrant is not required to be thread-safe. + +*/ +char *curlx_basename(char *path) +{ + /* Ignore all the details above for now and make a quick and simple + implementation here */ + char *s1; + char *s2; + + s1 = strrchr(path, '/'); + s2 = strrchr(path, '\\'); + + if(s1 && s2) + path = ((s1 > s2) ? s1 : s2) + 1; + else if(s1) + path = s1 + 1; + else if(s2) + path = s2 + 1; + + return path; +} + +#endif /* !HAVE_BASENAME */ diff --git a/vendor/curl/lib/curlx/binmode.h b/vendor/curl/lib/curlx/basename.h similarity index 67% rename from vendor/curl/lib/curlx/binmode.h rename to vendor/curl/lib/curlx/basename.h index 991cc89..fb79fed 100644 --- a/vendor/curl/lib/curlx/binmode.h +++ b/vendor/curl/lib/curlx/basename.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_TOOL_BINMODE_H -#define HEADER_CURL_TOOL_BINMODE_H +#ifndef HEADER_CURLX_BASENAME_H +#define HEADER_CURLX_BASENAME_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,17 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#if (defined(HAVE_SETMODE) || defined(HAVE__SETMODE)) && defined(O_BINARY) -/* Requires io.h and/or fcntl.h when available */ -#ifdef HAVE__SETMODE -# define CURLX_SET_BINMODE(stream) (void)_setmode(fileno(stream), O_BINARY) +#ifndef HAVE_BASENAME +char *curlx_basename(char *path); #else -# define CURLX_SET_BINMODE(stream) (void)setmode(fileno(stream), O_BINARY) -#endif -#else -# define CURLX_SET_BINMODE(stream) (void)stream + +#ifdef HAVE_LIBGEN_H +#include #endif -#endif /* HEADER_CURL_TOOL_BINMODE_H */ +#define curlx_basename(x) basename(x) +#endif /* !HAVE_BASENAME */ + +#endif /* HEADER_CURLX_BASENAME_H */ diff --git a/vendor/curl/lib/curlx/curlx.h b/vendor/curl/lib/curlx/curlx.h index 817acb0..79cffc8 100644 --- a/vendor/curl/lib/curlx/curlx.h +++ b/vendor/curl/lib/curlx/curlx.h @@ -31,53 +31,22 @@ * be. */ -#include "binmode.h" -/* "binmode.h" provides macro CURLX_SET_BINMODE() */ - -#include "nonblock.h" -/* "nonblock.h" provides curlx_nonblock() */ - -#include "multibyte.h" -/* "multibyte.h" provides these functions and macros: - - curlx_convert_UTF8_to_wchar() - curlx_convert_wchar_to_UTF8() - curlx_convert_UTF8_to_tchar() - curlx_convert_tchar_to_UTF8() -*/ - -#include "version_win32.h" -/* provides curlx_verify_windows_version() */ - -#include "strerr.h" -/* The curlx_strerror() function */ - -#include "strparse.h" -/* The curlx_str_* parsing functions */ - -#include "strcopy.h" -/* curlx_strcopy */ - -#include "dynbuf.h" -/* The curlx_dyn_* functions */ - -#include "fopen.h" -/* The curlx_f* functions */ - -#include "base64.h" -#include "timeval.h" -#include "timediff.h" - -#include "wait.h" -/* for curlx_wait_ms */ - -#include "winapi.h" -/* for curlx_winapi_strerror */ - -#include "inet_pton.h" -/* for curlx_inet_pton */ - -#include "inet_ntop.h" -/* for curlx_inet_ntop */ +#include "curlx/base64.h" /* for curlx_base64* */ +#include "curlx/basename.h" /* for curlx_basename() */ +#include "curlx/dynbuf.h" /* for curlx_dyn_*() */ +#include "curlx/fopen.h" /* for curlx_f*() */ +#include "curlx/inet_ntop.h" /* for curlx_inet_ntop() */ +#include "curlx/inet_pton.h" /* for curlx_inet_pton() */ +#include "curlx/multibyte.h" /* for curlx_convert_*() */ +#include "curlx/nonblock.h" /* for curlx_nonblock() */ +#include "curlx/strcopy.h" /* for curlx_strcopy() */ +#include "curlx/strdup.h" /* for curlx_memdup*() and curlx_tcsdup() */ +#include "curlx/strerr.h" /* for curlx_strerror() */ +#include "curlx/strparse.h" /* for curlx_str_* parsing functions */ +#include "curlx/timediff.h" /* for timediff_t type and related functions */ +#include "curlx/timeval.h" /* for curlx_now type and related functions */ +#include "curlx/version_win32.h" /* for curlx_verify_windows_version() */ +#include "curlx/wait.h" /* for curlx_wait_ms() */ +#include "curlx/winapi.h" /* for curlx_winapi_strerror() */ #endif /* HEADER_CURL_CURLX_H */ diff --git a/vendor/curl/lib/curlx/dynbuf.c b/vendor/curl/lib/curlx/dynbuf.c index 158d4d0..7045a1f 100644 --- a/vendor/curl/lib/curlx/dynbuf.c +++ b/vendor/curl/lib/curlx/dynbuf.c @@ -21,10 +21,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "dynbuf.h" -#include "../curl_printf.h" +#include "curlx/dynbuf.h" +#include "curl_printf.h" #define MIN_FIRST_ALLOC 32 @@ -102,8 +102,6 @@ static CURLcode dyn_nappend(struct dynbuf *s, } if(a != s->allc) { - /* this logic is not using Curl_saferealloc() to make the tool not have to - include that as well when it uses this code */ void *p = curlx_realloc(s->bufr, a); if(!p) { curlx_dyn_free(s); diff --git a/vendor/curl/lib/curlx/fopen.c b/vendor/curl/lib/curlx/fopen.c index e9eb6df..6d0ef7e 100644 --- a/vendor/curl/lib/curlx/fopen.c +++ b/vendor/curl/lib/curlx/fopen.c @@ -21,9 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "fopen.h" +#include "curlx/fopen.h" int curlx_fseek(void *stream, curl_off_t offset, int whence) { @@ -42,10 +42,10 @@ int curlx_fseek(void *stream, curl_off_t offset, int whence) #include /* for _SH_DENYNO */ -#include "multibyte.h" -#include "timeval.h" +#include "curlx/multibyte.h" +#include "curlx/timeval.h" -#ifdef CURLDEBUG +#ifdef CURL_MEMDEBUG /* * Use system allocators to avoid infinite recursion when called by curl's * memory tracker memdebug functions. @@ -405,7 +405,7 @@ FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fp) return result; } -int curlx_win32_stat(const char *path, struct_stat *buffer) +int curlx_win32_stat(const char *path, curlx_struct_stat *buffer) { int result = -1; TCHAR *fixed = NULL; @@ -439,8 +439,8 @@ int curlx_win32_stat(const char *path, struct_stat *buffer) #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES) || \ !defined(CURL_DISABLE_ALTSVC) /* rename() on Windows does not overwrite, so we cannot use it here. - MoveFileEx() will overwrite and is usually atomic, however it fails - when there are open handles to the file. */ + MoveFileEx() will overwrite and is usually atomic but fails when there are + open handles to the file. */ int curlx_win32_rename(const char *oldpath, const char *newpath) { int res = -1; /* fail */ diff --git a/vendor/curl/lib/curlx/fopen.h b/vendor/curl/lib/curlx/fopen.h index 53c079e..d89830e 100644 --- a/vendor/curl/lib/curlx/fopen.h +++ b/vendor/curl/lib/curlx/fopen.h @@ -23,9 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "multibyte.h" +#include "curlx/multibyte.h" #ifdef HAVE_FCNTL_H #include /* for open() and attributes */ @@ -34,6 +34,7 @@ int curlx_fseek(void *stream, curl_off_t offset, int whence); #ifdef _WIN32 +#include /* for _fstati64(), struct _stati64 */ #ifndef CURL_WINDOWS_UWP HANDLE curlx_CreateFile(const char *filename, DWORD dwDesiredAccess, @@ -43,25 +44,33 @@ HANDLE curlx_CreateFile(const char *filename, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); #endif /* !CURL_WINDOWS_UWP */ +#define curlx_fstat _fstati64 +#define curlx_struct_stat struct _stati64 FILE *curlx_win32_fopen(const char *filename, const char *mode); FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fh); -int curlx_win32_stat(const char *path, struct_stat *buffer); +int curlx_win32_stat(const char *path, curlx_struct_stat *buffer); int curlx_win32_open(const char *filename, int oflag, ...); int curlx_win32_rename(const char *oldpath, const char *newpath); -#define CURLX_FOPEN_LOW(fname, mode) curlx_win32_fopen(fname, mode) -#define CURLX_FREOPEN_LOW(fname, mode, fh) curlx_win32_freopen(fname, mode, fh) -#define curlx_stat(fname, stp) curlx_win32_stat(fname, stp) -#define curlx_open curlx_win32_open -#define curlx_rename curlx_win32_rename +#define CURLX_FOPEN_LOW curlx_win32_fopen +#define CURLX_FREOPEN_LOW curlx_win32_freopen +#define CURLX_FDOPEN_LOW _fdopen +#define curlx_stat curlx_win32_stat +#define curlx_open curlx_win32_open +#define curlx_close _close +#define curlx_rename curlx_win32_rename #else -#define CURLX_FOPEN_LOW fopen -#define CURLX_FREOPEN_LOW freopen -#define curlx_stat(fname, stp) stat(fname, stp) -#define curlx_open open -#define curlx_rename rename +#define curlx_fstat fstat +#define curlx_struct_stat struct stat +#define CURLX_FOPEN_LOW fopen +#define CURLX_FREOPEN_LOW freopen +#define CURLX_FDOPEN_LOW fdopen +#define curlx_stat stat +#define curlx_open open +#define curlx_close close +#define curlx_rename rename #endif -#ifdef CURLDEBUG +#ifdef CURL_MEMDEBUG #define curlx_fopen(file, mode) curl_dbg_fopen(file, mode, __LINE__, __FILE__) #define curlx_freopen(file, mode, fh) \ curl_dbg_freopen(file, mode, fh, __LINE__, __FILE__) @@ -71,7 +80,7 @@ int curlx_win32_rename(const char *oldpath, const char *newpath); #else #define curlx_fopen CURLX_FOPEN_LOW #define curlx_freopen CURLX_FREOPEN_LOW -#define curlx_fdopen fdopen +#define curlx_fdopen CURLX_FDOPEN_LOW #define curlx_fclose fclose #endif diff --git a/vendor/curl/lib/curlx/inet_ntop.c b/vendor/curl/lib/curlx/inet_ntop.c index 6bea5e1..803b988 100644 --- a/vendor/curl/lib/curlx/inet_ntop.c +++ b/vendor/curl/lib/curlx/inet_ntop.c @@ -16,7 +16,7 @@ * * SPDX-License-Identifier: ISC */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef HAVE_INET_NTOP @@ -30,9 +30,9 @@ #include #endif -#include "inet_ntop.h" -#include "snprintf.h" -#include "strcopy.h" +#include "curlx/inet_ntop.h" +#include "curlx/snprintf.h" +#include "curlx/strcopy.h" #define IN6ADDRSZ 16 /* #define INADDRSZ 4 */ @@ -166,7 +166,7 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) static const unsigned char ldigits[] = "0123456789abcdef"; unsigned int w = words[i]; - /* output lowercase 16bit hex number but ignore leading zeroes */ + /* output lowercase 16-bit hex number but ignore leading zeroes */ if(w & 0xf000) *tp++ = ldigits[(w & 0xf000) >> 12]; if(w & 0xff00) diff --git a/vendor/curl/lib/curlx/inet_ntop.h b/vendor/curl/lib/curlx/inet_ntop.h index 45b63d9..2e3419f 100644 --- a/vendor/curl/lib/curlx/inet_ntop.h +++ b/vendor/curl/lib/curlx/inet_ntop.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_INET_NTOP #ifdef HAVE_NETINET_IN_H @@ -37,7 +37,7 @@ #endif #ifdef __AMIGA__ #define curlx_inet_ntop(af, addr, buf, size) \ - (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)buf, \ + (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)(buf), \ (curl_socklen_t)(size)) #else #define curlx_inet_ntop(af, addr, buf, size) \ diff --git a/vendor/curl/lib/curlx/inet_pton.c b/vendor/curl/lib/curlx/inet_pton.c index 703a7b9..d3c53fb 100644 --- a/vendor/curl/lib/curlx/inet_pton.c +++ b/vendor/curl/lib/curlx/inet_pton.c @@ -17,9 +17,7 @@ * * SPDX-License-Identifier: ISC */ -#include "../curl_setup.h" - -#include "strparse.h" +#include "curl_setup.h" #ifndef HAVE_INET_PTON @@ -33,7 +31,8 @@ #include #endif -#include "inet_pton.h" +#include "curlx/inet_pton.h" +#include "curlx/strparse.h" #define IN6ADDRSZ 16 #define INADDRSZ 4 @@ -53,41 +52,7 @@ * 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); -static int inet_pton6(const char *src, unsigned char *dst); - -/* int - * inet_pton(af, src, dst) - * convert from presentation format (which usually means ASCII printable) - * to network format (which is usually some kind of binary format). - * return: - * 1 if the address was valid for the specified address family - * 0 if the address was not valid (`dst' is untouched in this case) - * -1 if some other error occurred (`dst' is untouched in this case, too) - * notice: - * On Windows we store the error in the thread errno, not - * in the Winsock error code. This is to avoid losing the - * actual last Winsock error. When this function returns - * -1, check errno not SOCKERRNO. - * author: - * Paul Vixie, 1996. - */ -int curlx_inet_pton(int af, const char *src, void *dst) -{ - switch(af) { - case AF_INET: - return inet_pton4(src, (unsigned char *)dst); - case AF_INET6: - return inet_pton6(src, (unsigned char *)dst); - default: - errno = SOCKEAFNOSUPPORT; - return -1; - } - /* NOTREACHED */ -} - -/* int - * inet_pton4(src, dst) +/* int inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. @@ -105,7 +70,7 @@ static int inet_pton4(const char *src, unsigned char *dst) octets = 0; tp = tmp; *tp = 0; - while((ch = *src++) != '\0') { + while((ch = (unsigned char)*src++) != '\0') { if(ISDIGIT(ch)) { unsigned int val = (*tp * 10) + (ch - '0'); @@ -135,8 +100,7 @@ static int inet_pton4(const char *src, unsigned char *dst) return 1; } -/* int - * inet_pton6(src, dst) +/* int inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. @@ -165,7 +129,7 @@ static int inet_pton6(const char *src, unsigned char *dst) curtok = src; saw_xdigit = 0; val = 0; - while((ch = *src++) != '\0') { + while((ch = (unsigned char)*src++) != '\0') { if(ISXDIGIT(ch)) { val <<= 4; val |= curlx_hexval(ch); @@ -225,4 +189,33 @@ static int inet_pton6(const char *src, unsigned char *dst) return 1; } +/* int inet_pton(af, src, dst) + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * return: + * 1 if the address was valid for the specified address family + * 0 if the address was not valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * notice: + * On Windows we store the error in the thread errno, not + * in the Winsock error code. This is to avoid losing the + * actual last Winsock error. When this function returns + * -1, check errno not SOCKERRNO. + * author: + * Paul Vixie, 1996. + */ +int curlx_inet_pton(int af, const char *src, void *dst) +{ + switch(af) { + case AF_INET: + return inet_pton4(src, (unsigned char *)dst); + case AF_INET6: + return inet_pton6(src, (unsigned char *)dst); + default: + errno = SOCKEAFNOSUPPORT; + return -1; + } + /* NOTREACHED */ +} + #endif /* HAVE_INET_PTON */ diff --git a/vendor/curl/lib/curlx/inet_pton.h b/vendor/curl/lib/curlx/inet_pton.h index fc3ae3d..02ae7f2 100644 --- a/vendor/curl/lib/curlx/inet_pton.h +++ b/vendor/curl/lib/curlx/inet_pton.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_INET_PTON #ifdef HAVE_NETINET_IN_H @@ -43,7 +43,7 @@ inet_pton(x, y, z) #endif #else -int curlx_inet_pton(int, const char *, void *); +int curlx_inet_pton(int af, const char *src, void *dst); #endif /* HAVE_INET_PTON */ #endif /* HEADER_CURL_INET_PTON_H */ diff --git a/vendor/curl/lib/curlx/multibyte.c b/vendor/curl/lib/curlx/multibyte.c index 529091a..715d2b8 100644 --- a/vendor/curl/lib/curlx/multibyte.c +++ b/vendor/curl/lib/curlx/multibyte.c @@ -21,11 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(_WIN32) && defined(UNICODE) -#include "multibyte.h" +#include "curlx/multibyte.h" /* * MultiByte conversions using Windows kernel32 library. diff --git a/vendor/curl/lib/curlx/multibyte.h b/vendor/curl/lib/curlx/multibyte.h index 26da0f0..8863fb0 100644 --- a/vendor/curl/lib/curlx/multibyte.h +++ b/vendor/curl/lib/curlx/multibyte.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32 diff --git a/vendor/curl/lib/curlx/nonblock.c b/vendor/curl/lib/curlx/nonblock.c index 96bf3db..6f6458f 100644 --- a/vendor/curl/lib/curlx/nonblock.c +++ b/vendor/curl/lib/curlx/nonblock.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_SYS_IOCTL_H #include @@ -35,7 +35,7 @@ #include #endif -#include "nonblock.h" +#include "curlx/nonblock.h" /* * curlx_nonblock() set the given socket to either blocking or non-blocking diff --git a/vendor/curl/lib/vquic/curl_osslq.h b/vendor/curl/lib/curlx/snprintf.c similarity index 58% rename from vendor/curl/lib/vquic/curl_osslq.h rename to vendor/curl/lib/curlx/snprintf.c index 7aa93d6..c733252 100644 --- a/vendor/curl/lib/vquic/curl_osslq.h +++ b/vendor/curl/lib/curlx/snprintf.c @@ -1,5 +1,3 @@ -#ifndef HEADER_CURL_VQUIC_CURL_OSSLQ_H -#define HEADER_CURL_VQUIC_CURL_OSSLQ_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,29 +21,29 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_OPENSSL_QUIC) && \ - defined(USE_NGHTTP3) - -#ifdef HAVE_NETINET_UDP_H -#include +#include "curlx/snprintf.h" + +#ifdef _WIN32 +#include + +/* Simplified wrapper for the Windows platform to use the correct symbol and + ensuring null-termination. Omit returning a length to keep it simple. */ +void curlx_win32_snprintf(char *buf, size_t maxlen, const char *fmt, ...) +{ + va_list ap; + if(!maxlen) + return; + va_start(ap, fmt); +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif - -struct Curl_cfilter; - -#include "../urldata.h" - -void Curl_osslq_ver(char *p, size_t len); - -CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai); - -bool Curl_conn_is_osslq(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); + /* !checksrc! disable BANNEDFUNC 1 */ + (void)vsnprintf(buf, maxlen, fmt, ap); +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop #endif - -#endif /* HEADER_CURL_VQUIC_CURL_OSSLQ_H */ + buf[maxlen - 1] = 0; + va_end(ap); +} +#endif /* _WIN32 */ diff --git a/vendor/curl/lib/curlx/snprintf.h b/vendor/curl/lib/curlx/snprintf.h index 266ea13..d6260e4 100644 --- a/vendor/curl/lib/curlx/snprintf.h +++ b/vendor/curl/lib/curlx/snprintf.h @@ -1,3 +1,5 @@ +#ifndef HEADER_CURLX_SNPRINTF_H +#define HEADER_CURLX_SNPRINTF_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -21,12 +23,18 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" /* Raw snprintf() for curlx */ +#ifdef _WIN32 +void curlx_win32_snprintf(char *buf, size_t maxlen, const char *fmt, ...) + CURL_PRINTF(3, 4); +#endif + #ifdef WITHOUT_LIBCURL /* when built for the test servers */ -#if defined(_MSC_VER) && (_MSC_VER < 1900) /* adjust for old MSVC */ -#define SNPRINTF _snprintf +#ifdef _WIN32 +#define SNPRINTF curlx_win32_snprintf #else #define SNPRINTF snprintf #endif @@ -34,3 +42,4 @@ #include #define SNPRINTF curl_msnprintf #endif /* WITHOUT_LIBCURL */ +#endif /* HEADER_CURLX_SNPRINTF_H */ diff --git a/vendor/curl/lib/curlx/strcopy.c b/vendor/curl/lib/curlx/strcopy.c index d6bff15..da137c3 100644 --- a/vendor/curl/lib/curlx/strcopy.c +++ b/vendor/curl/lib/curlx/strcopy.c @@ -21,9 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "strcopy.h" +#include "curlx/strcopy.h" /* * curlx_strcopy() is a replacement for strcpy. diff --git a/vendor/curl/lib/strdup.c b/vendor/curl/lib/curlx/strdup.c similarity index 64% rename from vendor/curl/lib/strdup.c rename to vendor/curl/lib/curlx/strdup.c index f666571..3c967db 100644 --- a/vendor/curl/lib/strdup.c +++ b/vendor/curl/lib/curlx/strdup.c @@ -27,53 +27,33 @@ #include #endif -#include "strdup.h" - -#ifndef HAVE_STRDUP -char *Curl_strdup(const char *str) -{ - size_t len; - char *newstr; - - if(!str) - return (char *)NULL; - - len = strlen(str) + 1; - - newstr = curlx_malloc(len); - if(!newstr) - return (char *)NULL; - - memcpy(newstr, str, len); - return newstr; -} -#endif +#include "curlx/strdup.h" #ifdef _WIN32 /*************************************************************************** * - * Curl_wcsdup(source) + * curlx_wcsdup(source) * * Copies the 'source' wchar string to a newly allocated buffer (that is - * returned). + * returned). Used by macro curlx_tcsdup(). * * Returns the new pointer or NULL on failure. * ***************************************************************************/ -wchar_t *Curl_wcsdup(const wchar_t *src) +wchar_t *curlx_wcsdup(const wchar_t *src) { size_t length = wcslen(src); if(length > (SIZE_MAX / sizeof(wchar_t)) - 1) return (wchar_t *)NULL; /* integer overflow */ - return (wchar_t *)Curl_memdup(src, (length + 1) * sizeof(wchar_t)); + return (wchar_t *)curlx_memdup(src, (length + 1) * sizeof(wchar_t)); } #endif /*************************************************************************** * - * Curl_memdup(source, length) + * curlx_memdup(source, length) * * Copies the 'source' data to a newly allocated buffer (that is * returned). Copies 'length' bytes. @@ -81,7 +61,7 @@ wchar_t *Curl_wcsdup(const wchar_t *src) * Returns the new pointer or NULL on failure. * ***************************************************************************/ -void *Curl_memdup(const void *src, size_t length) +void *curlx_memdup(const void *src, size_t length) { void *buffer = curlx_malloc(length); if(!buffer) @@ -94,7 +74,7 @@ void *Curl_memdup(const void *src, size_t length) /*************************************************************************** * - * Curl_memdup0(source, length) + * curlx_memdup0(source, length) * * Copies the 'source' string to a newly allocated buffer (that is returned). * Copies 'length' bytes then adds a null-terminator. @@ -102,7 +82,7 @@ void *Curl_memdup(const void *src, size_t length) * Returns the new pointer or NULL on failure. * ***************************************************************************/ -void *Curl_memdup0(const char *src, size_t length) +void *curlx_memdup0(const char *src, size_t length) { char *buf = (length < SIZE_MAX) ? curlx_malloc(length + 1) : NULL; if(!buf) @@ -114,26 +94,3 @@ void *Curl_memdup0(const char *src, size_t length) buf[length] = 0; return buf; } - -/*************************************************************************** - * - * Curl_saferealloc(ptr, size) - * - * Does a normal curlx_realloc(), but will free the data pointer if the realloc - * fails. If 'size' is non-zero, it will free the data and return a failure. - * - * This convenience function is provided and used to help us avoid a common - * mistake pattern when we could pass in a zero, catch the NULL return and end - * up free'ing the memory twice. - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -void *Curl_saferealloc(void *ptr, size_t size) -{ - void *datap = curlx_realloc(ptr, size); - if(size && !datap) - /* only free 'ptr' if size was non-zero */ - curlx_free(ptr); - return datap; -} diff --git a/vendor/curl/lib/strdup.h b/vendor/curl/lib/curlx/strdup.h similarity index 75% rename from vendor/curl/lib/strdup.h rename to vendor/curl/lib/curlx/strdup.h index cd01ffb..0c4b141 100644 --- a/vendor/curl/lib/strdup.h +++ b/vendor/curl/lib/curlx/strdup.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_STRDUP_H -#define HEADER_CURL_STRDUP_H +#ifndef HEADER_CURLX_STRDUP_H +#define HEADER_CURLX_STRDUP_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -25,13 +25,9 @@ ***************************************************************************/ #include "curl_setup.h" -#ifndef HAVE_STRDUP -char *Curl_strdup(const char *str); -#endif #ifdef _WIN32 -wchar_t *Curl_wcsdup(const wchar_t *src); +wchar_t *curlx_wcsdup(const wchar_t *src); /* for curlx_tcsdup() */ #endif -void *Curl_memdup(const void *src, size_t buffer_length); -void *Curl_saferealloc(void *ptr, size_t size); -void *Curl_memdup0(const char *src, size_t length); -#endif /* HEADER_CURL_STRDUP_H */ +void *curlx_memdup(const void *src, size_t length); +void *curlx_memdup0(const char *src, size_t length); +#endif /* HEADER_CURLX_STRDUP_H */ diff --git a/vendor/curl/lib/curlx/strerr.c b/vendor/curl/lib/curlx/strerr.c index 3f1b1f5..91a329e 100644 --- a/vendor/curl/lib/curlx/strerr.c +++ b/vendor/curl/lib/curlx/strerr.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_STRERROR_R # if (!defined(HAVE_POSIX_STRERROR_R) && \ @@ -31,10 +31,10 @@ # endif #endif -#include "winapi.h" -#include "snprintf.h" -#include "strerr.h" -#include "strcopy.h" +#include "curlx/winapi.h" +#include "curlx/snprintf.h" +#include "curlx/strerr.h" +#include "curlx/strcopy.h" #ifdef USE_WINSOCK /* This is a helper function for curlx_strerror that converts Winsock error @@ -43,17 +43,14 @@ */ static const char *get_winsock_error(int err, char *buf, size_t len) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *p; - size_t alen; -#endif + VERBOSE(const char *p); if(!len) return NULL; *buf = '\0'; -#ifdef CURL_DISABLE_VERBOSE_STRINGS +#ifndef CURLVERBOSE (void)err; return NULL; #else @@ -223,8 +220,7 @@ static const char *get_winsock_error(int err, char *buf, size_t len) default: return NULL; } - alen = strlen(p); - curlx_strcopy(buf, len, p, alen); + curlx_strcopy(buf, len, p, strlen(p)); return buf; #endif } @@ -294,7 +290,7 @@ const char *curlx_strerror(int err, char *buf, size_t buflen) */ { char buffer[256]; - char *msg = strerror_r(err, buffer, sizeof(buffer)); + const char *msg = strerror_r(err, buffer, sizeof(buffer)); if(msg && buflen > 1) SNPRINTF(buf, buflen, "%s", msg); else if(buflen > sizeof("Unknown error ") + 20) diff --git a/vendor/curl/lib/curlx/strparse.c b/vendor/curl/lib/curlx/strparse.c index f11b58b..138b3df 100644 --- a/vendor/curl/lib/curlx/strparse.c +++ b/vendor/curl/lib/curlx/strparse.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "strparse.h" +#include "curlx/strparse.h" void curlx_str_init(struct Curl_str *out) { @@ -140,7 +140,7 @@ int curlx_str_singlespace(const char **linep) /* given an ASCII character and max ascii, return TRUE if valid */ #define valid_digit(x, m) \ - (((x) >= '0') && ((x) <= m) && curlx_hexasciitable[(x) - '0']) + (((x) >= '0') && ((x) <= (m)) && curlx_hexasciitable[(x) - '0']) /* We use 16 for the zero index (and the necessary bitwise AND in the loop) to be able to have a non-zero value there to make valid_digit() able to @@ -172,7 +172,7 @@ static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max, /* special-case low max scenario because check needs to be different */ do { int n = curlx_hexval(*p++); - num = num * base + n; + num = (num * base) + n; if(num > max) return STRE_OVERFLOW; } while(valid_digit(*p, m)); @@ -182,7 +182,7 @@ static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max, int n = curlx_hexval(*p++); if(num > ((max - n) / base)) return STRE_OVERFLOW; - num = num * base + n; + num = (num * base) + n; } while(valid_digit(*p, m)); } *nump = num; diff --git a/vendor/curl/lib/curlx/strparse.h b/vendor/curl/lib/curlx/strparse.h index ae8fd19..514203d 100644 --- a/vendor/curl/lib/curlx/strparse.h +++ b/vendor/curl/lib/curlx/strparse.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #define STRE_OK 0 #define STRE_BIG 1 @@ -98,7 +98,8 @@ int curlx_str_cmp(struct Curl_str *str, const char *check); int curlx_str_nudge(struct Curl_str *str, size_t num); -int curlx_str_cspn(const char **linep, struct Curl_str *out, const char *cspn); +int curlx_str_cspn(const char **linep, struct Curl_str *out, + const char *reject); void curlx_str_trimblanks(struct Curl_str *out); void curlx_str_passblanks(const char **linep); diff --git a/vendor/curl/lib/curlx/timediff.c b/vendor/curl/lib/curlx/timediff.c index 694c8d8..2bf7869 100644 --- a/vendor/curl/lib/curlx/timediff.c +++ b/vendor/curl/lib/curlx/timediff.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "timediff.h" +#include "curlx/timediff.h" /* * Converts number of milliseconds into a timeval structure. diff --git a/vendor/curl/lib/curlx/timediff.h b/vendor/curl/lib/curlx/timediff.h index e19b0f4..1081c75 100644 --- a/vendor/curl/lib/curlx/timediff.h +++ b/vendor/curl/lib/curlx/timediff.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" /* Use a larger type even for 32-bit time_t systems so that we can keep microsecond accuracy in it */ diff --git a/vendor/curl/lib/curlx/timeval.c b/vendor/curl/lib/curlx/timeval.c index fa7e528..435804c 100644 --- a/vendor/curl/lib/curlx/timeval.c +++ b/vendor/curl/lib/curlx/timeval.c @@ -21,58 +21,30 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "timeval.h" +#include "curlx/timeval.h" #ifdef _WIN32 -#include "version_win32.h" -#include "../system_win32.h" +#include "system_win32.h" LARGE_INTEGER Curl_freq; -bool Curl_isVistaOrGreater; /* For tool or tests, we must initialize before calling curlx_now(). Providing this function here is wrong. */ void curlx_now_init(void) { - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) - Curl_isVistaOrGreater = true; - else - Curl_isVistaOrGreater = false; - QueryPerformanceFrequency(&Curl_freq); } /* In case of bug fix this function has a counterpart in tool_util.c */ void curlx_pnow(struct curltime *pnow) { - bool isVistaOrGreater; - isVistaOrGreater = Curl_isVistaOrGreater; - if(isVistaOrGreater) { /* QPC timer might have issues pre-Vista */ - LARGE_INTEGER count; - LARGE_INTEGER freq; - freq = Curl_freq; - DEBUGASSERT(freq.QuadPart); - QueryPerformanceCounter(&count); - pnow->tv_sec = (time_t)(count.QuadPart / freq.QuadPart); - pnow->tv_usec = (int)((count.QuadPart % freq.QuadPart) * 1000000 / - freq.QuadPart); - } - else { - /* Disable /analyze warning that GetTickCount64 is preferred */ -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:28159) -#endif - DWORD milliseconds = GetTickCount(); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - pnow->tv_sec = (time_t)(milliseconds / 1000); - pnow->tv_usec = (int)((milliseconds % 1000) * 1000); - } + LARGE_INTEGER count; + DEBUGASSERT(Curl_freq.QuadPart); + QueryPerformanceCounter(&count); + pnow->tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart); + pnow->tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 / + Curl_freq.QuadPart); } #elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \ @@ -81,19 +53,19 @@ void curlx_pnow(struct curltime *pnow) void curlx_pnow(struct curltime *pnow) { /* - ** clock_gettime() is granted to be increased monotonically when the - ** monotonic clock is queried. Time starting point is unspecified, it - ** could be the system start-up time, the Epoch, or something else, - ** in any case the time starting point does not change once that the - ** system has started up. - */ + * clock_gettime() is granted to be increased monotonically when the + * monotonic clock is queried. Time starting point is unspecified, it + * could be the system start-up time, the Epoch, or something else, + * in any case the time starting point does not change once that the + * system has started up. + */ struct timespec tsnow; /* - ** clock_gettime() may be defined by Apple's SDK as weak symbol thus - ** code compiles but fails during runtime if clock_gettime() is - ** called on unsupported OS version. - */ + * clock_gettime() may be defined by Apple's SDK as weak symbol thus + * code compiles but fails during runtime if clock_gettime() is + * called on unsupported OS version. + */ #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ (HAVE_BUILTIN_AVAILABLE == 1) bool have_clock_gettime = FALSE; @@ -124,10 +96,10 @@ void curlx_pnow(struct curltime *pnow) pnow->tv_usec = (int)(tsnow.tv_nsec / 1000); } /* - ** Even when the configure process has truly detected monotonic clock - ** availability, it might happen that it is not actually available at - ** runtime. When this occurs simply fallback to other time source. - */ + * Even when the configure process has truly detected monotonic clock + * availability, it might happen that it is not actually available at + * runtime. When this occurs, fallback to other time source. + */ #ifdef HAVE_GETTIMEOFDAY else { struct timeval now; @@ -145,17 +117,16 @@ void curlx_pnow(struct curltime *pnow) #elif defined(HAVE_MACH_ABSOLUTE_TIME) -#include #include void curlx_pnow(struct curltime *pnow) { /* - ** Monotonic timer on macOS is provided by mach_absolute_time(), which - ** returns time in Mach "absolute time units," which are platform-dependent. - ** To convert to nanoseconds, one must use conversion factors specified by - ** mach_timebase_info(). - */ + * Monotonic timer on macOS is provided by mach_absolute_time(), which + * returns time in Mach "absolute time units," which are platform-dependent. + * To convert to nanoseconds, one must use conversion factors specified by + * mach_timebase_info(). + */ static mach_timebase_info_data_t timebase; uint64_t usecs; @@ -176,10 +147,10 @@ void curlx_pnow(struct curltime *pnow) void curlx_pnow(struct curltime *pnow) { /* - ** gettimeofday() is not granted to be increased monotonically, due to - ** clock drifting and external source time synchronization it can jump - ** forward or backward in time. - */ + * gettimeofday() is not granted to be increased monotonically, due to + * clock drifting and external source time synchronization it can jump + * forward or backward in time. + */ struct timeval now; (void)gettimeofday(&now, NULL); pnow->tv_sec = now.tv_sec; @@ -191,8 +162,8 @@ void curlx_pnow(struct curltime *pnow) void curlx_pnow(struct curltime *pnow) { /* - ** time() returns the value of time in seconds since the Epoch. - */ + * time() returns the value of time in seconds since the Epoch. + */ pnow->tv_sec = time(NULL); pnow->tv_usec = 0; } @@ -220,10 +191,9 @@ timediff_t curlx_ptimediff_ms(const struct curltime *newer, return TIMEDIFF_T_MAX; else if(diff <= (TIMEDIFF_T_MIN / 1000)) return TIMEDIFF_T_MIN; - return diff * 1000 + (newer->tv_usec - older->tv_usec) / 1000; + return (diff * 1000) + ((newer->tv_usec - older->tv_usec) / 1000); } - timediff_t curlx_timediff_ms(struct curltime newer, struct curltime older) { return curlx_ptimediff_ms(&newer, &older); @@ -241,7 +211,7 @@ timediff_t curlx_timediff_ceil_ms(struct curltime newer, return TIMEDIFF_T_MAX; else if(diff <= (TIMEDIFF_T_MIN / 1000)) return TIMEDIFF_T_MIN; - return diff * 1000 + (newer.tv_usec - older.tv_usec + 999) / 1000; + return (diff * 1000) + ((newer.tv_usec - older.tv_usec + 999) / 1000); } /* @@ -256,7 +226,7 @@ timediff_t curlx_ptimediff_us(const struct curltime *newer, return TIMEDIFF_T_MAX; else if(diff <= (TIMEDIFF_T_MIN / 1000000)) return TIMEDIFF_T_MIN; - return diff * 1000000 + newer->tv_usec - older->tv_usec; + return (diff * 1000000) + newer->tv_usec - older->tv_usec; } timediff_t curlx_timediff_us(struct curltime newer, struct curltime older) diff --git a/vendor/curl/lib/curlx/timeval.h b/vendor/curl/lib/curlx/timeval.h index 4aab175..c01f95d 100644 --- a/vendor/curl/lib/curlx/timeval.h +++ b/vendor/curl/lib/curlx/timeval.h @@ -23,9 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "timediff.h" +#include "curlx/timediff.h" struct curltime { time_t tv_sec; /* seconds */ diff --git a/vendor/curl/lib/curlx/version_win32.c b/vendor/curl/lib/curlx/version_win32.c index a9a4ee3..296a38d 100644 --- a/vendor/curl/lib/curlx/version_win32.c +++ b/vendor/curl/lib/curlx/version_win32.c @@ -21,12 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32 -#include "version_win32.h" +#include "curlx/version_win32.h" +#ifndef CURL_WINDOWS_UWP /* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW) and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */ struct OUR_OSVERSIONINFOEXW { @@ -43,6 +44,24 @@ struct OUR_OSVERSIONINFOEXW { UCHAR wReserved; }; +typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN) + (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG); +static RTLVERIFYVERSIONINFO_FN s_pRtlVerifyVersionInfo; + +void curlx_verify_windows_init(void) +{ +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif + s_pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN, + GetProcAddress(GetModuleHandle(TEXT("ntdll")), "RtlVerifyVersionInfo")); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif +} +#endif /* !CURL_WINDOWS_UWP */ + /* * curlx_verify_windows_version() * @@ -115,24 +134,6 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; - typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN) - (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG); - static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo; - static bool onetime = TRUE; /* safe because first call is during init */ - - if(onetime) { -#if defined(__clang__) && __clang_major__ >= 16 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcast-function-type-strict" -#endif - pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN, - GetProcAddress(GetModuleHandle(TEXT("ntdll")), "RtlVerifyVersionInfo")); -#if defined(__clang__) && __clang_major__ >= 16 -#pragma clang diagnostic pop -#endif - onetime = FALSE; - } - switch(condition) { case VERSION_LESS_THAN: majorCondition = VER_LESS; @@ -203,8 +204,8 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, the real version always, so we use the Rtl variant of the function when possible. Note though the function signatures have underlying fundamental types that are the same, the return values are different. */ - if(pRtlVerifyVersionInfo) - matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); + if(s_pRtlVerifyVersionInfo) + matched = !s_pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); else matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm); @@ -222,11 +223,11 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition); dwTypeMask = VER_BUILDNUMBER; - if(pRtlVerifyVersionInfo) - matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); + if(s_pRtlVerifyVersionInfo) + matched = !s_pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); else matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, - dwTypeMask, cm); + dwTypeMask, cm); } #endif diff --git a/vendor/curl/lib/curlx/version_win32.h b/vendor/curl/lib/curlx/version_win32.h index a5fb8ff..c4a1c0f 100644 --- a/vendor/curl/lib/curlx/version_win32.h +++ b/vendor/curl/lib/curlx/version_win32.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32 @@ -43,6 +43,11 @@ typedef enum { PLATFORM_WINNT } PlatformIdentifier; +#ifdef CURL_WINDOWS_UWP +#define curlx_verify_windows_init() Curl_nop_stmt +#else +void curlx_verify_windows_init(void); +#endif /* This is used to verify if we are running on a specific Windows version */ bool curlx_verify_windows_version(const unsigned int majorVersion, const unsigned int minorVersion, diff --git a/vendor/curl/lib/curlx/wait.c b/vendor/curl/lib/curlx/wait.c index 2a9f54b..5278426 100644 --- a/vendor/curl/lib/curlx/wait.c +++ b/vendor/curl/lib/curlx/wait.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef HAVE_SELECT #error "We cannot compile without select() support." @@ -37,19 +37,19 @@ #include /* delay() */ #endif -#include "timediff.h" -#include "wait.h" +#include "curlx/timediff.h" +#include "curlx/wait.h" /* * Internal function used for waiting a specific amount of ms in * Curl_socket_check() and Curl_poll() when no file descriptor is provided to - * wait on, just being used to delay execution. Winsock select() and poll() - * timeout mechanisms need a valid socket descriptor in a not null file - * descriptor set to work. Waiting indefinitely with this function is not - * allowed, a zero or negative timeout value will return immediately. Timeout - * resolution, accuracy, as well as maximum supported value is system - * dependent, neither factor is a critical issue for the intended use of this - * function in the library. + * wait on, being used to delay execution. Winsock select() and poll() timeout + * mechanisms need a valid socket descriptor in a not null file descriptor set + * to work. Waiting indefinitely with this function is not allowed, a zero or + * negative timeout value will return immediately. Timeout resolution, + * accuracy, as well as maximum supported value is system dependent, neither + * factor is a critical issue for the intended use of this function in the + * library. * * Return values: * -1 = system call error, or invalid timeout value diff --git a/vendor/curl/lib/curlx/wait.h b/vendor/curl/lib/curlx/wait.h index c140194..9b9c273 100644 --- a/vendor/curl/lib/curlx/wait.h +++ b/vendor/curl/lib/curlx/wait.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" int curlx_wait_ms(timediff_t timeout_ms); diff --git a/vendor/curl/lib/curlx/warnless.c b/vendor/curl/lib/curlx/warnless.c index 8c1be2d..5fc36ac 100644 --- a/vendor/curl/lib/curlx/warnless.c +++ b/vendor/curl/lib/curlx/warnless.c @@ -21,9 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "warnless.h" +#include "curlx/warnless.h" #if defined(__INTEL_COMPILER) && defined(__unix__) @@ -49,9 +49,8 @@ #define CURL_MASK_SSIZE_T (CURL_MASK_USIZE_T >> 1) /* -** unsigned long to unsigned char -*/ - + * unsigned long to unsigned char + */ unsigned char curlx_ultouc(unsigned long ulnum) { #ifdef __INTEL_COMPILER @@ -68,9 +67,8 @@ unsigned char curlx_ultouc(unsigned long ulnum) } /* -** unsigned size_t to signed int -*/ - + * unsigned size_t to signed int + */ int curlx_uztosi(size_t uznum) { #ifdef __INTEL_COMPILER @@ -87,9 +85,8 @@ int curlx_uztosi(size_t uznum) } /* -** unsigned size_t to unsigned long -*/ - + * unsigned size_t to unsigned long + */ unsigned long curlx_uztoul(size_t uznum) { #ifdef __INTEL_COMPILER @@ -108,9 +105,8 @@ unsigned long curlx_uztoul(size_t uznum) } /* -** unsigned size_t to unsigned int -*/ - + * unsigned size_t to unsigned int + */ unsigned int curlx_uztoui(size_t uznum) { #ifdef __INTEL_COMPILER @@ -129,9 +125,8 @@ unsigned int curlx_uztoui(size_t uznum) } /* -** signed long to signed int -*/ - + * signed long to signed int + */ int curlx_sltosi(long slnum) { #ifdef __INTEL_COMPILER @@ -151,9 +146,8 @@ int curlx_sltosi(long slnum) } /* -** signed long to unsigned int -*/ - + * signed long to unsigned int + */ unsigned int curlx_sltoui(long slnum) { #ifdef __INTEL_COMPILER @@ -173,9 +167,8 @@ unsigned int curlx_sltoui(long slnum) } /* -** signed long to unsigned short -*/ - + * signed long to unsigned short + */ unsigned short curlx_sltous(long slnum) { #ifdef __INTEL_COMPILER @@ -193,9 +186,8 @@ unsigned short curlx_sltous(long slnum) } /* -** unsigned size_t to signed ssize_t -*/ - + * unsigned size_t to signed ssize_t + */ ssize_t curlx_uztosz(size_t uznum) { #ifdef __INTEL_COMPILER @@ -212,9 +204,8 @@ ssize_t curlx_uztosz(size_t uznum) } /* -** signed curl_off_t to unsigned size_t -*/ - + * signed curl_off_t to unsigned size_t + */ size_t curlx_sotouz(curl_off_t sonum) { #ifdef __INTEL_COMPILER @@ -231,9 +222,8 @@ size_t curlx_sotouz(curl_off_t sonum) } /* -** signed ssize_t to signed int -*/ - + * signed ssize_t to signed int + */ int curlx_sztosi(ssize_t sznum) { #ifdef __INTEL_COMPILER @@ -253,9 +243,8 @@ int curlx_sztosi(ssize_t sznum) } /* -** unsigned int to unsigned short -*/ - + * unsigned int to unsigned short + */ unsigned short curlx_uitous(unsigned int uinum) { #ifdef __INTEL_COMPILER @@ -272,9 +261,8 @@ unsigned short curlx_uitous(unsigned int uinum) } /* -** signed int to unsigned size_t -*/ - + * signed int to unsigned size_t + */ size_t curlx_sitouz(int sinum) { #ifdef __INTEL_COMPILER diff --git a/vendor/curl/lib/curlx/warnless.h b/vendor/curl/lib/curlx/warnless.h index 8a62ccf..3d0341e 100644 --- a/vendor/curl/lib/curlx/warnless.h +++ b/vendor/curl/lib/curlx/warnless.h @@ -69,6 +69,6 @@ bool curlx_sotouz_fits(curl_off_t sonum, size_t *puznum); /* Convert a long to size_t, return FALSE if negative or too large * and set 0 */ -bool curlx_sltouz(long sznum, size_t *puznum); +bool curlx_sltouz(long slnum, size_t *puznum); #endif /* HEADER_CURL_WARNLESS_H */ diff --git a/vendor/curl/lib/curlx/winapi.c b/vendor/curl/lib/curlx/winapi.c index b46a5bf..3417df2 100644 --- a/vendor/curl/lib/curlx/winapi.c +++ b/vendor/curl/lib/curlx/winapi.c @@ -21,16 +21,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" /* * curlx_winapi_strerror: * Variant of curlx_strerror if the error code is definitely Windows API. */ #ifdef _WIN32 -#include "winapi.h" -#include "snprintf.h" -#include "strcopy.h" +#include "curlx/winapi.h" +#include "curlx/snprintf.h" +#include "curlx/strcopy.h" /* This is a helper function for curlx_strerror that converts Windows API error * codes (GetLastError) to error messages. @@ -39,30 +39,27 @@ const char *curlx_get_winapi_error(DWORD err, char *buf, size_t buflen) { char *p; - wchar_t wbuf[256]; - DWORD wlen; if(!buflen) return NULL; - *buf = '\0'; - *wbuf = L'\0'; - /* We return the local codepage version of the error string because if it is output to the user's terminal it will likely be with functions which expect the local codepage (eg fprintf, failf, infof). */ - wlen = FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err, - LANG_NEUTRAL, wbuf, CURL_ARRAYSIZE(wbuf), NULL); - if(wlen && !wcstombs_s(NULL, buf, buflen, wbuf, wlen)) { - /* Truncate multiple lines */ - p = strchr(buf, '\n'); - if(p) { - if(p > buf && *(p - 1) == '\r') - *(p - 1) = '\0'; - else - *p = '\0'; - } + if(!FormatMessageA((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err, + LANG_NEUTRAL, buf, (DWORD)buflen, NULL)) { + *buf = '\0'; + return NULL; + } + + /* Truncate multiple lines */ + p = strchr(buf, '\n'); + if(p) { + if(p > buf && *(p - 1) == '\r') + *(p - 1) = '\0'; + else + *p = '\0'; } return *buf ? buf : NULL; @@ -78,7 +75,7 @@ const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen) *buf = '\0'; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(!curlx_get_winapi_error(err, buf, buflen)) { #if defined(__GNUC__) && __GNUC__ >= 7 #pragma GCC diagnostic push diff --git a/vendor/curl/lib/cw-out.c b/vendor/curl/lib/cw-out.c index 69fb344..554c32e 100644 --- a/vendor/curl/lib/cw-out.c +++ b/vendor/curl/lib/cw-out.c @@ -101,22 +101,6 @@ struct cw_out_ctx { BIT(errored); }; -static CURLcode cw_out_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes); -static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer); -static CURLcode cw_out_init(struct Curl_easy *data, - struct Curl_cwriter *writer); - -const struct Curl_cwtype Curl_cwt_out = { - "cw-out", - NULL, - cw_out_init, - cw_out_write, - cw_out_close, - sizeof(struct cw_out_ctx) -}; - static CURLcode cw_out_init(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -198,6 +182,8 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, size_t nwritten; CURLcode result; + NOVERBOSE((void)otype); + DEBUGASSERT(data->conn); *pnwritten = 0; Curl_set_in_callback(data, TRUE); @@ -206,11 +192,11 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu", blen, (otype == CW_OUT_HDS) ? "header" : "body", nwritten); - if(CURL_WRITEFUNC_PAUSE == nwritten) { - if(data->conn->handler->flags & PROTOPT_NONETWORK) { + if(nwritten == CURL_WRITEFUNC_PAUSE) { + if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* Protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it cannot pause since the - transfer is not done using the "normal" procedure. */ + actually only FILE:// now, and it cannot pause since the transfer is + not done using the "normal" procedure. */ failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; } @@ -219,7 +205,7 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, result = Curl_xfer_pause_recv(data, TRUE); return result ? result : CURLE_AGAIN; } - else if(CURL_WRITEFUNC_ERROR == nwritten) { + else if(nwritten == CURL_WRITEFUNC_ERROR) { failf(data, "client returned ERROR on write of %zu bytes", blen); return CURLE_WRITE_ERROR; } @@ -455,6 +441,15 @@ static CURLcode cw_out_write(struct Curl_easy *data, return CURLE_OK; } +const struct Curl_cwtype Curl_cwt_out = { + "cw-out", + NULL, + cw_out_init, + cw_out_write, + cw_out_close, + sizeof(struct cw_out_ctx) +}; + bool Curl_cw_out_is_paused(struct Curl_easy *data) { struct Curl_cwriter *cw_out; @@ -465,7 +460,7 @@ bool Curl_cw_out_is_paused(struct Curl_easy *data) return FALSE; ctx = (struct cw_out_ctx *)cw_out; - return ctx->paused; + return (bool)ctx->paused; } static CURLcode cw_out_flush(struct Curl_easy *data, diff --git a/vendor/curl/lib/cw-pause.c b/vendor/curl/lib/cw-pause.c index 481b9fb..ec61187 100644 --- a/vendor/curl/lib/cw-pause.c +++ b/vendor/curl/lib/cw-pause.c @@ -34,7 +34,7 @@ /* body dynbuf sizes */ #define CW_PAUSE_BUF_CHUNK (16 * 1024) /* when content decoding, write data in chunks */ -#define CW_PAUSE_DEC_WRITE_CHUNK (4096) +#define CW_PAUSE_DEC_WRITE_CHUNK 4096 struct cw_pause_buf { struct cw_pause_buf *next; @@ -70,23 +70,6 @@ struct cw_pause_ctx { size_t buf_total; }; -static CURLcode cw_pause_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes); -static void cw_pause_close(struct Curl_easy *data, - struct Curl_cwriter *writer); -static CURLcode cw_pause_init(struct Curl_easy *data, - struct Curl_cwriter *writer); - -const struct Curl_cwtype Curl_cwt_pause = { - "cw-pause", - NULL, - cw_pause_init, - cw_pause_write, - cw_pause_close, - sizeof(struct cw_pause_ctx) -}; - static CURLcode cw_pause_init(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -220,6 +203,15 @@ static CURLcode cw_pause_write(struct Curl_easy *data, return result; } +const struct Curl_cwtype Curl_cwt_pause = { + "cw-pause", + NULL, + cw_pause_init, + cw_pause_write, + cw_pause_close, + sizeof(struct cw_pause_ctx) +}; + CURLcode Curl_cw_pause_flush(struct Curl_easy *data) { struct Curl_cwriter *cw_pause; diff --git a/vendor/curl/lib/dict.c b/vendor/curl/lib/dict.c index 5839e80..fcdea91 100644 --- a/vendor/curl/lib/dict.c +++ b/vendor/curl/lib/dict.c @@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "dict.h" #ifndef CURL_DISABLE_DICT @@ -51,11 +53,9 @@ #include #endif -#include "urldata.h" #include "transfer.h" #include "curl_trc.h" #include "escape.h" -#include "dict.h" #define DICT_MATCH "/MATCH:" #define DICT_MATCH2 "/M:" @@ -65,41 +65,6 @@ #define DICT_DEFINE3 "/LOOKUP:" -/* - * Forward declarations. - */ - -static CURLcode dict_do(struct Curl_easy *data, bool *done); - -/* - * DICT protocol handler. - */ - -const struct Curl_handler Curl_handler_dict = { - "dict", /* scheme */ - ZERO_NULL, /* setup_connection */ - dict_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_DICT, /* defport */ - CURLPROTO_DICT, /* protocol */ - CURLPROTO_DICT, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ -}; - #define DYN_DICT_WORD 10000 static char *unescape_word(const char *input) { @@ -306,4 +271,44 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) curlx_free(path); return result; } + +/* + * DICT protocol + */ +static const struct Curl_protocol Curl_protocol_dict = { + ZERO_NULL, /* setup_connection */ + dict_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_DICT */ + +/* + * DICT protocol handler. + */ +const struct Curl_scheme Curl_scheme_dict = { + "dict", /* scheme */ +#ifdef CURL_DISABLE_DICT + ZERO_NULL, +#else + &Curl_protocol_dict, +#endif + CURLPROTO_DICT, /* protocol */ + CURLPROTO_DICT, /* family */ + PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */ + PORT_DICT, /* defport */ +}; diff --git a/vendor/curl/lib/dict.h b/vendor/curl/lib/dict.h index 4025c8b..b5d6947 100644 --- a/vendor/curl/lib/dict.h +++ b/vendor/curl/lib/dict.h @@ -23,8 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef CURL_DISABLE_DICT -extern const struct Curl_handler Curl_handler_dict; -#endif +extern const struct Curl_scheme Curl_scheme_dict; #endif /* HEADER_CURL_DICT_H */ diff --git a/vendor/curl/lib/doh.c b/vendor/curl/lib/doh.c index 9052fab..4488d42 100644 --- a/vendor/curl/lib/doh.c +++ b/vendor/curl/lib/doh.c @@ -28,19 +28,19 @@ #include "urldata.h" #include "curl_addrinfo.h" #include "doh.h" - #include "curl_trc.h" +#include "httpsrr.h" #include "multiif.h" #include "url.h" #include "connect.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "curlx/dynbuf.h" -#include "escape.h" +#include "escape.h" /* for Curl_hexencode() */ #include "urlapi-int.h" #define DNS_CLASS_IN 0x01 -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static const char * const errors[] = { "", "Bad label", @@ -65,7 +65,7 @@ static const char *doh_strerror(DOHcode code) return "bad error code"; } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ /* @unittest 1655 */ @@ -113,7 +113,7 @@ UNITTEST DOHcode doh_req_encode(const char *host, if(len < expected_len) return DOH_TOO_SMALL_BUFFER; - *dnsp++ = 0; /* 16 bit id */ + *dnsp++ = 0; /* 16-bit id */ *dnsp++ = 0; *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */ *dnsp++ = '\0'; /* |RA| Z | RCODE | */ @@ -129,7 +129,7 @@ UNITTEST DOHcode doh_req_encode(const char *host, /* encode each label and store it in the QNAME */ while(*hostp) { size_t labellen; - char *dot = strchr(hostp, '.'); + const char *dot = strchr(hostp, '.'); if(dot) labellen = dot - hostp; else @@ -152,10 +152,10 @@ UNITTEST DOHcode doh_req_encode(const char *host, *dnsp++ = 0; /* append zero-length label for root */ /* There are assigned TYPE codes beyond 255: use range [1..65535] */ - *dnsp++ = (unsigned char)(255 & (dnstype >> 8)); /* upper 8 bit TYPE */ - *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */ + *dnsp++ = (unsigned char)(255 & (dnstype >> 8)); /* upper 8-bit TYPE */ + *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8-bit TYPE */ - *dnsp++ = '\0'; /* upper 8 bit CLASS */ + *dnsp++ = '\0'; /* upper 8-bit CLASS */ *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */ *olen = dnsp - orig; @@ -181,7 +181,7 @@ static size_t doh_probe_write_cb(char *contents, size_t size, size_t nmemb, return realsize; } -#if defined(USE_HTTPSRR) && defined(DEBUGBUILD) +#if defined(USE_HTTPSRR) && defined(DEBUGBUILD) && defined(CURLVERBOSE) /* doh_print_buf truncates if the hex string will be more than this */ #define LOCAL_PB_HEXMAX 400 @@ -201,7 +201,6 @@ static void doh_print_buf(struct Curl_easy *data, infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr); else infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr); - return; } #endif @@ -304,8 +303,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data, goto error; } - timeout_ms = Curl_timeleft_ms(data, TRUE); - if(timeout_ms <= 0) { + timeout_ms = Curl_timeleft_ms(data); + if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; goto error; } @@ -324,9 +323,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, /* pass in the struct pointer via a local variable to please coverity and the gcc typecheck helpers */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - doh->state.feat = &Curl_trc_feat_dns; -#endif + VERBOSE(doh->state.feat = &Curl_trc_feat_dns); ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_probe_write_cb); @@ -495,7 +492,7 @@ CURLcode Curl_doh(struct Curl_easy *data, const char *hostname, #endif #ifdef USE_HTTPSRR - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { + if(conn->scheme->protocol & PROTO_FAMILY_HTTP) { /* Only use HTTPS RR for HTTP(S) transfers */ char *qname = NULL; if(port != PORT_HTTPS) { @@ -594,7 +591,7 @@ static DOHcode doh_store_https(const unsigned char *doh, int index, /* silently ignore RRs over the limit */ if(d->numhttps_rrs < DOH_MAX_HTTPS) { struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs]; - h->val = Curl_memdup(&doh[index], len); + h->val = curlx_memdup(&doh[index], len); if(!h->val) return DOH_OUT_OF_MEM; h->len = len; @@ -692,10 +689,10 @@ static DOHcode doh_rdata(const unsigned char *doh, return rc; break; case CURL_DNS_TYPE_DNAME: - /* explicit for clarity; just skip; rely on synthesized CNAME */ + /* explicit for clarity; skip; rely on synthesized CNAME */ break; default: - /* unsupported type, just skip it */ + /* unsupported type, skip it */ break; } return DOH_OK; @@ -757,9 +754,9 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, return DOH_DNS_OUT_OF_RANGE; type = doh_get16bit(doh, index); - if((type != CURL_DNS_TYPE_CNAME) /* may be synthesized from DNAME */ - && (type != CURL_DNS_TYPE_DNAME) /* if present, accept and ignore */ - && (type != dnstype)) + if((type != CURL_DNS_TYPE_CNAME) && /* may be synthesized from DNAME */ + (type != CURL_DNS_TYPE_DNAME) && /* if present, accept and ignore */ + (type != dnstype)) /* Not the same type as was asked for nor CNAME nor DNAME */ return DOH_DNS_UNEXPECTED_TYPE; index += 2; @@ -853,7 +850,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, return DOH_OK; /* ok */ } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static void doh_show(struct Curl_easy *data, const struct dohentry *d) { @@ -886,13 +883,13 @@ static void doh_show(struct Curl_easy *data, } #ifdef USE_HTTPSRR for(i = 0; i < d->numhttps_rrs; i++) { -# ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) doh_print_buf(data, "DoH HTTPS", d->https_rrs[i].val, d->https_rrs[i].len); -# else +#else infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len); -# endif - } #endif + } +#endif /* USE_HTTPSRR */ for(i = 0; i < d->numcname; i++) { infof(data, "CNAME: %s", curlx_dyn_ptr(&d->cname[i])); } @@ -982,7 +979,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, addr = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); - addr->sin_family = (CURL_SA_FAMILY_T)addrtype; + addr->sin_family = addrtype; addr->sin_port = htons((unsigned short)port); break; @@ -991,7 +988,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, addr6 = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); - addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype; + addr6->sin6_family = addrtype; addr6->sin6_port = htons((unsigned short)port); break; #endif @@ -1009,7 +1006,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, return result; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static const char *doh_type2name(DNStype dnstype) { switch(dnstype) { @@ -1051,9 +1048,9 @@ UNITTEST void de_cleanup(struct dohentry *d) * The encoding here is defined in * https://datatracker.ietf.org/doc/html/rfc1035#section-3.1 * - * The input buffer pointer will be modified so it points to - * just after the end of the DNS name encoding on output. (And - * that is why it is an "unsigned char **" :-) + * The input buffer pointer will be modified so it points to after the end of + * the DNS name encoding on output. (And that is why it is an "unsigned char + * **" :-) */ static CURLcode doh_decode_rdata_name(const unsigned char **buf, size_t *remaining, char **dnsname) @@ -1157,7 +1154,7 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, return result; } -#ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) UNITTEST void doh_print_httpsrr(struct Curl_easy *data, struct Curl_https_rrinfo *hrr); @@ -1193,7 +1190,6 @@ UNITTEST void doh_print_httpsrr(struct Curl_easy *data, } else infof(data, "HTTPS RR: no ipv6hints"); - return; } # endif #endif @@ -1233,12 +1229,10 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, rc[slot] = doh_resp_decode(curlx_dyn_uptr(&p->body), curlx_dyn_len(&p->body), p->dnstype, &de); -#ifndef CURL_DISABLE_VERBOSE_STRINGS if(rc[slot]) { CURL_TRC_DNS(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), doh_type2name(p->dnstype), dohp->host); } -#endif } /* next slot */ result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ @@ -1257,7 +1251,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, goto error; /* we got a response, create a dns entry. */ - dns = Curl_dnscache_mk_entry(data, ai, dohp->host, 0, dohp->port, FALSE); + dns = Curl_dnscache_mk_entry(data, &ai, dohp->host, 0, + dohp->port, FALSE); if(dns) { /* Now add and HTTPSRR information if we have */ #ifdef USE_HTTPSRR @@ -1271,19 +1266,17 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, goto error; } infof(data, "Some HTTPS RR to process"); -# ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) doh_print_httpsrr(data, hrr); -# endif +#endif dns->hinfo = hrr; } -#endif +#endif /* USE_HTTPSRR */ /* and add the entry to the cache */ data->state.async.dns = dns; result = Curl_dnscache_add(data, dns); *dnsp = data->state.async.dns; } - else - Curl_freeaddrinfo(ai); } /* address processing done */ /* All done */ diff --git a/vendor/curl/lib/doh.h b/vendor/curl/lib/doh.h index 43bc323..3eba4df 100644 --- a/vendor/curl/lib/doh.h +++ b/vendor/curl/lib/doh.h @@ -23,26 +23,20 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "urldata.h" -#include "curl_addrinfo.h" -#ifdef USE_HTTPSRR -# include -# include "httpsrr.h" -#endif #ifndef CURL_DISABLE_DOH typedef enum { DOH_OK, - DOH_DNS_BAD_LABEL, /* 1 */ - DOH_DNS_OUT_OF_RANGE, /* 2 */ - DOH_DNS_LABEL_LOOP, /* 3 */ - DOH_TOO_SMALL_BUFFER, /* 4 */ - DOH_OUT_OF_MEM, /* 5 */ - DOH_DNS_RDATA_LEN, /* 6 */ - DOH_DNS_MALFORMAT, /* 7 */ - DOH_DNS_BAD_RCODE, /* 8 - no such name */ + DOH_DNS_BAD_LABEL, /* 1 */ + DOH_DNS_OUT_OF_RANGE, /* 2 */ + DOH_DNS_LABEL_LOOP, /* 3 */ + DOH_TOO_SMALL_BUFFER, /* 4 */ + DOH_OUT_OF_MEM, /* 5 */ + DOH_DNS_RDATA_LEN, /* 6 */ + DOH_DNS_MALFORMAT, /* 7 */ + DOH_DNS_BAD_RCODE, /* 8 - no such name */ DOH_DNS_UNEXPECTED_TYPE, /* 9 */ DOH_DNS_UNEXPECTED_CLASS, /* 10 */ DOH_NO_CONTENT, /* 11 */ @@ -120,7 +114,7 @@ CURLcode Curl_doh(struct Curl_easy *data, const char *hostname, int port, int ip_version); CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns); + struct Curl_dns_entry **dnsp); #define DOH_MAX_ADDR 24 #define DOH_MAX_CNAME 4 diff --git a/vendor/curl/lib/dynhds.c b/vendor/curl/lib/dynhds.c index c69d312..7424a57 100644 --- a/vendor/curl/lib/dynhds.c +++ b/vendor/curl/lib/dynhds.c @@ -26,11 +26,6 @@ #include "dynhds.h" #include "strcase.h" -#ifdef USE_NGHTTP2 -#include -#include -#endif /* USE_NGHTTP2 */ - static struct dynhds_entry *entry_new(const char *name, size_t namelen, const char *value, size_t valuelen, int opts) diff --git a/vendor/curl/lib/dynhds.h b/vendor/curl/lib/dynhds.h index 170721c..e30eb45 100644 --- a/vendor/curl/lib/dynhds.h +++ b/vendor/curl/lib/dynhds.h @@ -50,7 +50,7 @@ struct dynhds { int opts; }; -#define DYNHDS_OPT_NONE (0) +#define DYNHDS_OPT_NONE 0 #define DYNHDS_OPT_LOWERCASE (1 << 0) /** @@ -153,16 +153,14 @@ CURLcode Curl_dynhds_cadd(struct dynhds *dynhds, const char *name, const char *value); /** - * Add a single header from an HTTP/1.1 formatted line at the end. Line - * may contain a delimiting CRLF or just LF. Any characters after - * that will be ignored. + * Add a single header from an HTTP/1.1 formatted line at the end. Line may + * contain a delimiting CRLF or LF. Any characters after that will be ignored. */ CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line); /** - * Add a single header from an HTTP/1.1 formatted line at the end. Line - * may contain a delimiting CRLF or just LF. Any characters after - * that will be ignored. + * Add a single header from an HTTP/1.1 formatted line at the end. Line may + * contain a delimiting CRLF or LF. Any characters after that will be ignored. */ CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, const char *line, size_t line_len); @@ -175,7 +173,6 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf); #ifdef USE_NGHTTP2 -#include #include nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount); diff --git a/vendor/curl/lib/easy.c b/vendor/curl/lib/easy.c index 398b20e..d05674e 100644 --- a/vendor/curl/lib/easy.c +++ b/vendor/curl/lib/easy.c @@ -51,7 +51,7 @@ #include "url.h" #include "getinfo.h" #include "hostip.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "easyif.h" #include "multiif.h" #include "multi_ev.h" @@ -94,21 +94,6 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; #endif -/* - * strdup (and other memory functions) is redefined in complicated - * ways, but at this point it must be defined as the system-supplied strdup - * so the callback pointer is initialized correctly. - */ -#ifdef HAVE_STRDUP -#ifdef _WIN32 -#define system_strdup _strdup -#else -#define system_strdup strdup -#endif -#else -#define system_strdup Curl_strdup -#endif - #if defined(_MSC_VER) && defined(_DLL) # pragma warning(push) # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ @@ -121,7 +106,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; -curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; +curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)CURLX_STRDUP_LOW; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; #if defined(_MSC_VER) && defined(_DLL) @@ -146,7 +131,7 @@ static CURLcode global_init(long flags, bool memoryfuncs) Curl_cmalloc = (curl_malloc_callback)malloc; Curl_cfree = (curl_free_callback)free; Curl_crealloc = (curl_realloc_callback)realloc; - Curl_cstrdup = (curl_strdup_callback)system_strdup; + Curl_cstrdup = (curl_strdup_callback)CURLX_STRDUP_LOW; Curl_ccalloc = (curl_calloc_callback)calloc; } @@ -361,7 +346,7 @@ CURL *curl_easy_init(void) } global_init_unlock(); - /* We use curl_open() with undefined URL so far */ + /* We use Curl_open() with undefined URL so far */ result = Curl_open(&data); if(result) { DEBUGF(curl_mfprintf(stderr, "Error: Curl_open failed\n")); @@ -618,7 +603,9 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) if(!pollrc) { /* timeout! */ ev->ms = 0; - /* curl_mfprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */ +#if 0 + curl_mfprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); +#endif mresult = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &ev->running_handles); } @@ -737,7 +724,7 @@ static CURLcode easy_transfer(struct Curl_multi *multi) * easy handle, destroys the multi handle and returns the easy handle's return * code. * - * REALITY: it cannot just create and destroy the multi handle that easily. It + * REALITY: it cannot create and destroy the multi handle that easily. It * needs to keep it around since if this easy handle is used again by this * function, the same multi handle must be reused so that the same pools and * caches can be used. @@ -750,7 +737,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) struct Curl_multi *multi; CURLMcode mresult; CURLcode result = CURLE_OK; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -807,8 +794,8 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) /* assign this after curl_multi_add_handle() */ data->multi_easy = multi; - sigpipe_init(&pipe_st); - sigpipe_apply(data, &pipe_st); + sigpipe_init(&sigpipe_ctx); + sigpipe_apply(data, &sigpipe_ctx); /* run the transfer */ result = events ? easy_events(multi) : easy_transfer(multi); @@ -817,7 +804,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) a failure here, room for future improvement! */ (void)curl_multi_remove_handle(multi, data); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); /* The multi handle is kept alive, owned by the easy handle */ return result; @@ -851,10 +838,10 @@ void curl_easy_cleanup(CURL *ptr) { struct Curl_easy *data = ptr; if(GOOD_EASY_HANDLE(data)) { - SIGPIPE_VARIABLE(pipe_st); - sigpipe_ignore(data, &pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; + sigpipe_ignore(data, &sigpipe_ctx); Curl_close(&data); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); } } @@ -891,8 +878,9 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) /* Copy src->set into dst->set first, then deal with the strings afterwards */ dst->set = src->set; - Curl_mime_initpart(&dst->set.mimepost); - +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + dst->set.mimepostp = NULL; +#endif /* clear all dest string and blob pointers first, in case we error out mid-function */ memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); @@ -918,17 +906,28 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) if(src->set.postfieldsize == -1) dst->set.str[i] = curlx_strdup(src->set.str[i]); else - /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ - dst->set.str[i] = Curl_memdup(src->set.str[i], - curlx_sotouz(src->set.postfieldsize)); + /* postfieldsize is curl_off_t, curlx_memdup() takes a size_t ... */ + dst->set.str[i] = curlx_memdup(src->set.str[i], + curlx_sotouz(src->set.postfieldsize)); if(!dst->set.str[i]) return CURLE_OUT_OF_MEMORY; /* point to the new copy */ dst->set.postfields = dst->set.str[i]; } - /* Duplicate mime data. */ - result = Curl_mime_duppart(dst, &dst->set.mimepost, &src->set.mimepost); +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + if(src->set.mimepostp) { + /* Duplicate mime data. Get a mimepost struct for the clone as well */ + dst->set.mimepostp = curlx_malloc(sizeof(*dst->set.mimepostp)); + if(!dst->set.mimepostp) + return CURLE_OUT_OF_MEMORY; + + Curl_mime_initpart(dst->set.mimepostp); + result = Curl_mime_duppart(dst, dst->set.mimepostp, src->set.mimepostp); + if(result) + return result; + } +#endif if(src->set.resolve) dst->state.resolve = dst->set.resolve; @@ -1275,7 +1274,7 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, { CURLcode result; struct connectdata *c = NULL; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; *n = 0; result = easy_connection(data, &c); @@ -1287,9 +1286,9 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, needs to be reattached */ Curl_attach_connection(data, c); - sigpipe_ignore(data, &pipe_st); + sigpipe_ignore(data, &sigpipe_ctx); result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n); - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); if(result && result != CURLE_AGAIN) return CURLE_SEND_ERROR; diff --git a/vendor/curl/lib/easy_lock.h b/vendor/curl/lib/easy_lock.h index 10c84c6..cefb01d 100644 --- a/vendor/curl/lib/easy_lock.h +++ b/vendor/curl/lib/easy_lock.h @@ -27,7 +27,7 @@ #define GLOBAL_INIT_IS_THREADSAFE -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 +#ifdef _WIN32 #define curl_simple_lock SRWLOCK #define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT @@ -72,8 +72,6 @@ static CURL_INLINE void curl_simple_lock_lock(curl_simple_lock *lock) __builtin_ia32_pause(); #elif defined(__aarch64__) __asm__ volatile("yield" ::: "memory"); -#elif defined(_WIN32) - Sleep(1); #elif defined(HAVE_SCHED_YIELD) sched_yield(); #endif diff --git a/vendor/curl/lib/escape.c b/vendor/curl/lib/escape.c index afdf18c..be44e97 100644 --- a/vendor/curl/lib/escape.c +++ b/vendor/curl/lib/escape.c @@ -63,7 +63,7 @@ char *curl_easy_escape(CURL *data, const char *string, int inlength) if(length > SIZE_MAX / 16) return NULL; - curlx_dyn_init(&d, length * 3 + 1); + curlx_dyn_init(&d, (length * 3) + 1); while(length--) { /* treat the characters unsigned */ diff --git a/vendor/curl/lib/escape.h b/vendor/curl/lib/escape.h index 2ea06c4..9f9a89e 100644 --- a/vendor/curl/lib/escape.h +++ b/vendor/curl/lib/escape.h @@ -24,7 +24,7 @@ * ***************************************************************************/ /* Escape and unescape URL encoding in strings. The functions return a new - * allocated string or NULL if an error occurred. */ + * allocated string or NULL if an error occurred. */ enum urlreject { REJECT_NADA = 2, diff --git a/vendor/curl/lib/fake_addrinfo.c b/vendor/curl/lib/fake_addrinfo.c index bf5a448..d8b1398 100644 --- a/vendor/curl/lib/fake_addrinfo.c +++ b/vendor/curl/lib/fake_addrinfo.c @@ -67,8 +67,8 @@ static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead) for(ai = aihead->nodes; ai != NULL; ai = ai->ai_next) { size_t ss_size; size_t namelen = name ? strlen(name) + 1 : 0; - /* ignore elements with unsupported address family, */ - /* settle family-specific sockaddr structure size. */ + /* ignore elements with unsupported address family, + settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); else if(ai->ai_family == AF_INET6) @@ -90,8 +90,8 @@ static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead) return NULL; } - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ + /* copy each structure member individually, member ordering, + size, or padding might be different for each platform. */ ca->ai_flags = ai->ai_flags; ca->ai_family = ai->ai_family; diff --git a/vendor/curl/lib/fake_addrinfo.h b/vendor/curl/lib/fake_addrinfo.h index db701be..07d5b6d 100644 --- a/vendor/curl/lib/fake_addrinfo.h +++ b/vendor/curl/lib/fake_addrinfo.h @@ -29,8 +29,8 @@ #include #endif -#if defined(CURLDEBUG) && defined(USE_ARES) && defined(HAVE_GETADDRINFO) && \ - (ARES_VERSION >= 0x011a00) /* >= 1.26.0 */ +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \ + defined(USE_ARES) && (ARES_VERSION >= 0x011a00) /* >= 1.26.0 */ #define USE_FAKE_GETADDRINFO 1 #endif diff --git a/vendor/curl/lib/file.c b/vendor/curl/lib/file.c index 982f254..1c3f663 100644 --- a/vendor/curl/lib/file.c +++ b/vendor/curl/lib/file.c @@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "file.h" #ifndef CURL_DISABLE_FILE @@ -53,12 +55,10 @@ #include #endif -#include "urldata.h" #include "progress.h" #include "sendf.h" #include "curl_trc.h" #include "escape.h" -#include "file.h" #include "multiif.h" #include "transfer.h" #include "url.h" @@ -82,55 +82,12 @@ struct FILEPROTO { int fd; /* open file descriptor to read from! */ }; -/* - * Forward declarations. - */ - -static CURLcode file_do(struct Curl_easy *data, bool *done); -static CURLcode file_done(struct Curl_easy *data, - CURLcode status, bool premature); -static CURLcode file_connect(struct Curl_easy *data, bool *done); -static CURLcode file_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); -static CURLcode file_setup_connection(struct Curl_easy *data, - struct connectdata *conn); - -/* - * FILE scheme handler. - */ - -const struct Curl_handler Curl_handler_file = { - "file", /* scheme */ - file_setup_connection, /* setup_connection */ - file_do, /* do_it */ - file_done, /* done */ - ZERO_NULL, /* do_more */ - file_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - file_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - 0, /* defport */ - CURLPROTO_FILE, /* protocol */ - CURLPROTO_FILE, /* family */ - PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */ -}; - static void file_cleanup(struct FILEPROTO *file) { Curl_safefree(file->freepath); file->path = NULL; if(file->fd != -1) { - close(file->fd); + curlx_close(file->fd); file->fd = -1; } } @@ -158,6 +115,19 @@ static CURLcode file_setup_connection(struct Curl_easy *data, return CURLE_OK; } +static CURLcode file_done(struct Curl_easy *data, + CURLcode status, bool premature) +{ + struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); + (void)status; + (void)premature; + + if(file) + file_cleanup(file); + + return CURLE_OK; +} + /* * file_connect() gets called from Curl_protocol_connect() to allow us to * do protocol-specific actions at connect-time. We emulate a @@ -234,7 +204,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) return CURLE_URL_MALFORMAT; } - #ifdef AMIGA_FILESYSTEM +#ifdef AMIGA_FILESYSTEM /* * A leading slash in an AmigaDOS path denotes the parent * directory, and hence we block this as it is relative. @@ -257,10 +227,10 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) fd = curlx_open(real_path, O_RDONLY); } } - #else +#else fd = curlx_open(real_path, O_RDONLY); file->path = real_path; - #endif +#endif #endif curlx_free(file->freepath); file->freepath = real_path; /* free this when done */ @@ -276,19 +246,6 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) return CURLE_OK; } -static CURLcode file_done(struct Curl_easy *data, - CURLcode status, bool premature) -{ - struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); - (void)status; - (void)premature; - - if(file) - file_cleanup(file); - - return CURLE_OK; -} - static CURLcode file_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead_connection) @@ -313,7 +270,7 @@ static CURLcode file_upload(struct Curl_easy *data, CURLcode result = CURLE_OK; char *xfer_ulbuf; size_t xfer_ulblen; - struct_stat file_stat; + curlx_struct_stat file_stat; const char *sendbuf; bool eos = FALSE; @@ -354,8 +311,8 @@ static CURLcode file_upload(struct Curl_easy *data, /* treat the negative resume offset value as the case of "-" */ if(data->state.resume_from < 0) { - if(fstat(fd, &file_stat)) { - close(fd); + if(curlx_fstat(fd, &file_stat)) { + curlx_close(fd); failf(data, "cannot get the size of %s", file->path); return CURLE_WRITE_ERROR; } @@ -410,7 +367,7 @@ static CURLcode file_upload(struct Curl_easy *data, result = Curl_pgrsUpdate(data); out: - close(fd); + curlx_close(fd); Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf); return result; @@ -433,9 +390,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) */ struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); CURLcode result = CURLE_OK; - struct_stat statbuf; /* struct_stat instead of struct stat just to allow the - Windows version to have a different struct without - having to redefine the simple word 'stat' */ + curlx_struct_stat statbuf; curl_off_t expected_size = -1; bool size_known; bool fstated = FALSE; @@ -454,7 +409,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) fd = file->fd; /* VMS: This only works reliable for STREAMLF files */ - if(fstat(fd, &statbuf) != -1) { + if(curlx_fstat(fd, &statbuf) != -1) { if(!S_ISDIR(statbuf.st_mode)) expected_size = statbuf.st_size; /* and store the modification time */ @@ -560,13 +515,8 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(data->state.resume_from) { if(!S_ISDIR(statbuf.st_mode)) { -#ifdef __AMIGA__ - if(data->state.resume_from != - lseek(fd, (off_t)data->state.resume_from, SEEK_SET)) -#else if(data->state.resume_from != - lseek(fd, data->state.resume_from, SEEK_SET)) -#endif + curl_lseek(fd, data->state.resume_from, SEEK_SET)) return CURLE_BAD_DOWNLOAD_RESUME; } else { @@ -648,4 +598,40 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) return result; } +static const struct Curl_protocol Curl_protocol_file = { + file_setup_connection, /* setup_connection */ + file_do, /* do_it */ + file_done, /* done */ + ZERO_NULL, /* do_more */ + file_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + file_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif + +/* + * FILE scheme handler. + */ +const struct Curl_scheme Curl_scheme_file = { + "file", /* scheme */ +#ifdef CURL_DISABLE_FILE + ZERO_NULL, +#else + &Curl_protocol_file, #endif + CURLPROTO_FILE, /* protocol */ + CURLPROTO_FILE, /* family */ + PROTOPT_NONETWORK | PROTOPT_NOURLQUERY, /* flags */ + 0 /* defport */ +}; diff --git a/vendor/curl/lib/file.h b/vendor/curl/lib/file.h index cb3552d..a306544 100644 --- a/vendor/curl/lib/file.h +++ b/vendor/curl/lib/file.h @@ -23,8 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef CURL_DISABLE_FILE -extern const struct Curl_handler Curl_handler_file; -#endif +extern const struct Curl_scheme Curl_scheme_file; #endif /* HEADER_CURL_FILE_H */ diff --git a/vendor/curl/lib/formdata.c b/vendor/curl/lib/formdata.c index 8c4dc48..3770d28 100644 --- a/vendor/curl/lib/formdata.c +++ b/vendor/curl/lib/formdata.c @@ -31,7 +31,7 @@ struct Curl_easy; #include "urldata.h" /* for struct Curl_easy */ #include "mime.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "bufref.h" #include "curlx/fopen.h" @@ -168,10 +168,9 @@ static void free_formlist(struct FormInfo *ptr) * * Stores a formpost parameter and builds the appropriate linked list. * - * Has two principal functionalities: using files and byte arrays as - * post parts. Byte arrays are either copied or just the pointer is stored - * (as the user requests) while for files only the filename and not the - * content is stored. + * Has two principal functionalities: using files and byte arrays as post + * parts. Byte arrays are either copied or the pointer is stored (as the user + * requests) while for files only the filename and not the content is stored. * * While you may have only one byte array for each name, multiple filenames * are allowed (and because of this feature CURLFORM_END is needed after @@ -304,13 +303,13 @@ static CURLFORMcode FormAdd(struct curl_httppost **httppost, struct FormInfo *first_form, *curr, *form = NULL; CURLFORMcode retval = CURL_FORMADD_OK; CURLformoption option; - struct curl_forms *forms = NULL; + const struct curl_forms *forms = NULL; char *avalue = NULL; struct curl_httppost *newchain = NULL; struct curl_httppost *lastnode = NULL; #define form_ptr_arg(t) (forms ? (t)(void *)avalue : va_arg(params, t)) -#ifdef HAVE_STDINT_H +#ifdef HAVE_UINTPTR_T #define form_int_arg(t) (forms ? (t)(uintptr_t)avalue : va_arg(params, t)) #else #define form_int_arg(t) (forms ? (t)(void *)avalue : va_arg(params, t)) @@ -667,7 +666,7 @@ void curl_formfree(struct curl_httppost *form) struct curl_httppost *next; if(!form) - /* no form to free, just get out of this */ + /* no form to free, get out of this */ return; do { @@ -696,7 +695,7 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) if(!name || !len) return curl_mime_name(part, name); - zname = Curl_memdup0(name, len); + zname = curlx_memdup0(name, len); if(!zname) return CURLE_OUT_OF_MEMORY; res = curl_mime_name(part, zname); @@ -710,8 +709,8 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) * mime part at '*finalform'. * * This function will not do a failf() for the potential memory failures but - * should for all other errors it spots. Just note that this function MAY get - * a NULL pointer in the 'data' argument. + * should for all other errors it spots. Note that this function MAY get a + * NULL pointer in the 'data' argument. */ CURLcode Curl_getformdata(CURL *data, @@ -840,8 +839,7 @@ CURLcode Curl_getformdata(CURL *data, return result; } -#else -/* if disabled */ +#else /* if disabled */ CURLFORMcode curl_formadd(struct curl_httppost **httppost, struct curl_httppost **last_post, ...) { diff --git a/vendor/curl/lib/formdata.h b/vendor/curl/lib/formdata.h index ce592a8..33d7d0a 100644 --- a/vendor/curl/lib/formdata.h +++ b/vendor/curl/lib/formdata.h @@ -47,7 +47,7 @@ struct FormInfo { }; CURLcode Curl_getformdata(CURL *data, - curl_mimepart *, + curl_mimepart *finalform, struct curl_httppost *post, curl_read_callback fread_func); #endif /* CURL_DISABLE_FORM_API */ diff --git a/vendor/curl/lib/ftp.c b/vendor/curl/lib/ftp.c index 12b8d4a..6047759 100644 --- a/vendor/curl/lib/ftp.c +++ b/vendor/curl/lib/ftp.c @@ -22,6 +22,7 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_FTP @@ -39,8 +40,8 @@ #include #endif -#include "urldata.h" #include "sendf.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "if2ip.h" #include "hostip.h" @@ -48,7 +49,6 @@ #include "transfer.h" #include "escape.h" #include "ftp.h" -#include "fileinfo.h" #include "ftplistparser.h" #include "curl_range.h" #include "strcase.h" @@ -64,7 +64,7 @@ #include "multiif.h" #include "url.h" #include "http_proxy.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "curlx/strerr.h" #include "curlx/strparse.h" @@ -78,16 +78,13 @@ /* macro to check for a three-digit ftp status code at the start of the given string */ #define STATUSCODE(line) \ - (ISDIGIT(line[0]) && ISDIGIT(line[1]) && ISDIGIT(line[2])) + (ISDIGIT((line)[0]) && ISDIGIT((line)[1]) && ISDIGIT((line)[2])) /* macro to check for the last line in an FTP server response */ -#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3])) +#define LASTLINE(line) (STATUSCODE(line) && (' ' == (line)[3])) -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define ftp_pasv_verbose(a, b, c, d) Curl_nop_stmt -#define FTP_CSTATE(c) ((void)(c), "") -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - /* for tracing purposes */ +#ifdef CURLVERBOSE +/* for tracing purposes */ static const char * const ftp_state_names[] = { "STOP", "WAIT220", @@ -129,7 +126,7 @@ static const char * const ftp_state_names[] = { }; #define FTP_CSTATE(ftpc) ((ftpc) ? ftp_state_names[(ftpc)->state] : "???") -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ /* This is the ONLY way to change FTP state! */ static void ftp_state_low(struct Curl_easy *data, @@ -140,22 +137,16 @@ static void ftp_state_low(struct Curl_easy *data, #endif ) { -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#ifdef DEBUGBUILD - (void)lineno; -#endif -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - if(ftpc->state != newstate) + if(ftpc->state != newstate) { #ifdef DEBUGBUILD + NOVERBOSE((void)lineno); CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_CSTATE(ftpc), ftp_state_names[newstate], lineno); #else CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_CSTATE(ftpc), ftp_state_names[newstate]); #endif -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ - + } ftpc->state = newstate; } @@ -166,23 +157,6 @@ static void ftp_state_low(struct Curl_easy *data, #define ftp_state(x, y, z) ftp_state_low(x, y, z, __LINE__) #endif /* DEBUGBUILD */ -static CURLcode ftp_sendquote(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct curl_slist *quote); -static CURLcode ftp_quit(struct Curl_easy *data, struct ftp_conn *ftpc); -static CURLcode ftp_parse_url_path(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp); -static CURLcode ftp_regular_transfer(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool *done); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void ftp_pasv_verbose(struct Curl_easy *data, - struct Curl_addrinfo *ai, - char *newhost, /* ASCII version */ - int port); -#endif static CURLcode ftp_state_mdtm(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp); @@ -194,118 +168,8 @@ static CURLcode ftp_nb_type(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp, bool ascii, ftpstate newstate); -static int ftp_need_type(struct ftp_conn *ftpc, bool ascii); -static CURLcode ftp_do(struct Curl_easy *data, bool *done); -static CURLcode ftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode ftp_connect(struct Curl_easy *data, bool *done); -static CURLcode ftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection); -static CURLcode ftp_do_more(struct Curl_easy *data, int *completed); -static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode ftp_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode ftp_domore_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode ftp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode ftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode init_wc_data(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp); -static CURLcode wc_statemach(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp); -static void wc_data_dtor(void *ptr); -static CURLcode ftp_state_retr(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - curl_off_t filesize); -static CURLcode ftp_readresp(struct Curl_easy *data, - struct ftp_conn *ftpc, - int sockindex, - struct pingpong *pp, - int *ftpcode, - size_t *size); -static CURLcode ftp_dophase_done(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool connected); - -/* - * FTP protocol handler. - */ - -const struct Curl_handler Curl_handler_ftp = { - "ftp", /* scheme */ - ftp_setup_connection, /* setup_connection */ - ftp_do, /* do_it */ - ftp_done, /* done */ - ftp_do_more, /* do_more */ - ftp_connect, /* connect_it */ - ftp_multi_statemach, /* connecting */ - ftp_doing, /* doing */ - ftp_pollset, /* proto_pollset */ - ftp_pollset, /* doing_pollset */ - ftp_domore_pollset, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ftp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_FTP, /* defport */ - CURLPROTO_FTP, /* protocol */ - CURLPROTO_FTP, /* family */ - PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | - PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | - PROTOPT_WILDCARD | PROTOPT_SSL_REUSE | - PROTOPT_CONN_REUSE /* flags */ -}; - -#ifdef USE_SSL -/* - * FTPS protocol handler. - */ - -const struct Curl_handler Curl_handler_ftps = { - "ftps", /* scheme */ - ftp_setup_connection, /* setup_connection */ - ftp_do, /* do_it */ - ftp_done, /* done */ - ftp_do_more, /* do_more */ - ftp_connect, /* connect_it */ - ftp_multi_statemach, /* connecting */ - ftp_doing, /* doing */ - ftp_pollset, /* proto_pollset */ - ftp_pollset, /* doing_pollset */ - ftp_domore_pollset, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ftp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_FTPS, /* defport */ - CURLPROTO_FTPS, /* protocol */ - CURLPROTO_FTP, /* family */ - PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | - PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD | - PROTOPT_CONN_REUSE /* flags */ -}; -#endif - -static void close_secondarysocket(struct Curl_easy *data, - struct ftp_conn *ftpc) -{ - (void)ftpc; - CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_CSTATE(ftpc)); - Curl_conn_close(data, SECONDARYSOCKET); - Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); -} +static CURLcode getftpresponse(struct Curl_easy *data, size_t *nreadp, + int *ftpcodep); static void freedirs(struct ftp_conn *ftpc) { @@ -315,1898 +179,1595 @@ static void freedirs(struct ftp_conn *ftpc) ftpc->file = NULL; } -#ifdef CURL_PREFER_LF_LINEENDS -/* - * Lineend Conversions - * On ASCII transfers, e.g. directory listings, we might get lines - * ending in '\r\n' and we prefer just '\n'. - * We might also get a lonely '\r' which we convert into a '\n'. - */ -struct ftp_cw_lc_ctx { - struct Curl_cwriter super; - bool newline_pending; -}; - -static CURLcode ftp_cw_lc_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t blen) +static size_t numof_slashes(const char *str) { - static const char nl = '\n'; - struct ftp_cw_lc_ctx *ctx = writer->ctx; - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - - if(!ftpc) - return CURLE_FAILED_INIT; - - if(!(type & CLIENTWRITE_BODY) || ftpc->transfertype != 'A') - return Curl_cwriter_write(data, writer->next, type, buf, blen); - - /* ASCII mode BODY data, convert lineends */ - while(blen) { - /* do not pass EOS when writing parts */ - int chunk_type = (type & ~CLIENTWRITE_EOS); - const char *cp; - size_t chunk_len; - CURLcode result; - - if(ctx->newline_pending) { - if(buf[0] != '\n') { - /* previous chunk ended in '\r' and we do not see a '\n' in this one, - * need to write a newline. */ - result = Curl_cwriter_write(data, writer->next, chunk_type, &nl, 1); - if(result) - return result; - } - /* either we just wrote the newline or it is part of the next - * chunk of bytes we write. */ - ctx->newline_pending = FALSE; - } - - cp = memchr(buf, '\r', blen); - if(!cp) - break; - - /* write the bytes before the '\r', excluding the '\r' */ - chunk_len = cp - buf; - if(chunk_len) { - result = Curl_cwriter_write(data, writer->next, chunk_type, - buf, chunk_len); - if(result) - return result; - } - /* skip the '\r', we now have a newline pending */ - buf = cp + 1; - blen = blen - chunk_len - 1; - ctx->newline_pending = TRUE; - } - - /* Any remaining data does not contain a '\r' */ - if(blen) { - DEBUGASSERT(!ctx->newline_pending); - return Curl_cwriter_write(data, writer->next, type, buf, blen); - } - else if(type & CLIENTWRITE_EOS) { - /* EndOfStream, if we have a trailing cr, now is the time to write it */ - if(ctx->newline_pending) { - ctx->newline_pending = FALSE; - return Curl_cwriter_write(data, writer->next, type, &nl, 1); + const char *slashPos; + size_t num = 0; + do { + slashPos = strchr(str, '/'); + if(slashPos) { + ++num; + str = slashPos + 1; } - /* Always pass on the EOS type indicator */ - return Curl_cwriter_write(data, writer->next, type, buf, 0); - } - return CURLE_OK; + } while(slashPos); + return num; } -static const struct Curl_cwtype ftp_cw_lc = { - "ftp-lineconv", - NULL, - Curl_cwriter_def_init, - ftp_cw_lc_write, - Curl_cwriter_def_close, - sizeof(struct ftp_cw_lc_ctx) -}; - -#endif /* CURL_PREFER_LF_LINEENDS */ - -static CURLcode getftpresponse(struct Curl_easy *data, size_t *nread, - int *ftpcode); +#define FTP_MAX_DIR_DEPTH 1000 /*********************************************************************** * - * ftp_check_ctrl_on_data_wait() + * ftp_parse_url_path() + * + * Parse the URL path into separate path components. * */ -static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data, - struct ftp_conn *ftpc) +static CURLcode ftp_parse_url_path(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) { - struct connectdata *conn = data->conn; - curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET]; - struct pingpong *pp = &ftpc->pp; - size_t nread; - int ftpcode; - bool response = FALSE; + const char *slashPos = NULL; + const char *fileName = NULL; + CURLcode result = CURLE_OK; + const char *rawPath = NULL; /* url-decoded "raw" path */ + size_t pathLen = 0; - /* First check whether there is a cached response from server */ - if(curlx_dyn_len(&pp->recvbuf)) { - const char *l = curlx_dyn_ptr(&pp->recvbuf); - if(!ISDIGIT(*l) || (*l > '3')) { - /* Data connection could not be established, let's return */ - infof(data, "There is negative response in cache while serv connect"); - (void)getftpresponse(data, &nread, &ftpcode); - return CURLE_FTP_ACCEPT_FAILED; - } - } + ftpc->ctl_valid = FALSE; + ftpc->cwdfail = FALSE; - if(pp->overflow) - /* there is pending control data still in the buffer to read */ - response = TRUE; - else { - int socketstate = SOCKET_READABLE(ctrl_sock, 0); - /* see if the connection request is already here */ - switch(socketstate) { - case -1: /* error */ - /* let's die here */ - failf(data, "Error while waiting for server connect"); - return CURLE_FTP_ACCEPT_FAILED; - default: - if(socketstate & CURL_CSELECT_IN) - response = TRUE; - break; - } + if(ftpc->rawpath) + freedirs(ftpc); + /* url-decode ftp path before further evaluation */ + result = Curl_urldecode(ftp->path, 0, &ftpc->rawpath, &pathLen, REJECT_CTRL); + if(result) { + failf(data, "path contains control characters"); + return result; } + rawPath = ftpc->rawpath; - if(response) { - infof(data, "Ctrl conn has data while waiting for data conn"); - if(pp->overflow > 3) { - const char *r = curlx_dyn_ptr(&pp->recvbuf); - size_t len = curlx_dyn_len(&pp->recvbuf); - - DEBUGASSERT((pp->overflow + pp->nfinal) <= curlx_dyn_len(&pp->recvbuf)); - /* move over the most recently handled response line */ - r += pp->nfinal; - len -= pp->nfinal; + switch(data->set.ftp_filemethod) { + case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ - if((len > 3) && LASTLINE(r)) { - curl_off_t status; - if(!curlx_str_number(&r, &status, 999) && (status == 226)) { - /* funny timing situation where we get the final message on the - control connection before traffic on the data connection has been - noticed. Leave the 226 in there and use this as a trigger to read - the data socket. */ - infof(data, "Got 226 before data activity"); - return CURLE_OK; - } - } - } + if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) + fileName = rawPath; /* this is a full file path */ + /* + else: ftpc->file is not used anywhere other than for operations on + a file. In other words, never for directory operations. + So we can safely leave filename as NULL here and use it as a + argument in dir/file decisions. + */ + break; - (void)getftpresponse(data, &nread, &ftpcode); + case FTPFILE_SINGLECWD: + slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* get path before last slash, except for / */ + size_t dirlen = slashPos - rawPath; + if(dirlen == 0) + dirlen = 1; - infof(data, "FTP code: %03d", ftpcode); + ftpc->dirs = curlx_calloc(1, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) + return CURLE_OUT_OF_MEMORY; - if(ftpcode / 100 > 3) - return CURLE_FTP_ACCEPT_FAILED; + ftpc->dirs[0].start = 0; + ftpc->dirs[0].len = (int)dirlen; + ftpc->dirdepth = 1; /* we consider it to be a single directory */ + fileName = slashPos + 1; /* rest is filename */ + } + else + fileName = rawPath; /* filename only (or empty) */ + break; - return CURLE_WEIRD_SERVER_REPLY; - } + default: /* allow pretty much anything */ + case FTPFILE_MULTICWD: { + /* current position: begin of next path component */ + const char *curPos = rawPath; - return CURLE_OK; -} + /* number of entries to allocate for the 'dirs' array */ + size_t dirAlloc = numof_slashes(rawPath); -/*********************************************************************** - * - * ftp_initiate_transfer() - * - * After connection from server is accepted this function is called to - * setup transfer parameters and initiate the data transfer. - * - */ -static CURLcode ftp_initiate_transfer(struct Curl_easy *data, - struct ftp_conn *ftpc) -{ - CURLcode result = CURLE_OK; - bool connected; + if(dirAlloc >= FTP_MAX_DIR_DEPTH) + /* suspiciously deep directory hierarchy */ + return CURLE_URL_MALFORMAT; - CURL_TRC_FTP(data, "ftp_initiate_transfer()"); - result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected); - if(result || !connected) - return result; + if(dirAlloc) { + ftpc->dirs = curlx_calloc(dirAlloc, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) + return CURLE_OUT_OF_MEMORY; - if(data->state.upload) { - /* When we know we are uploading a specified file, we can get the file - size prior to the actual upload. */ - Curl_pgrsSetUploadSize(data, data->state.infilesize); + /* parse the URL path into separate path components */ + while(dirAlloc--) { + const char *spos = strchr(curPos, '/'); + size_t clen = spos - curPos; - /* set the SO_SNDBUF for the secondary socket for those who need it */ - Curl_sndbuf_init(data->conn->sock[SECONDARYSOCKET]); + /* path starts with a slash: add that as a directory */ + if(!clen && (ftpc->dirdepth == 0)) + ++clen; - /* FTP upload, shutdown DATA, ignore shutdown errors, as we rely - * on the server response on the CONTROL connection. */ - Curl_xfer_setup_send(data, SECONDARYSOCKET); - Curl_xfer_set_shutdown(data, TRUE, TRUE); - } - else { - /* FTP download, shutdown, do not ignore errors */ - Curl_xfer_setup_recv(data, SECONDARYSOCKET, data->req.size); - Curl_xfer_set_shutdown(data, TRUE, FALSE); + /* we skip empty path components, like "x//y" since the FTP command + CWD requires a parameter and a non-existent parameter a) does not + work on many servers and b) has no effect on the others. */ + if(clen) { + ftpc->dirs[ftpc->dirdepth].start = (int)(curPos - rawPath); + ftpc->dirs[ftpc->dirdepth].len = (int)clen; + ftpc->dirdepth++; + } + curPos = spos + 1; + } + } + fileName = curPos; /* the rest is the filename (or empty) */ } + break; + } /* switch */ - ftpc->pp.pending_resp = TRUE; /* expect server response */ - ftp_state(data, ftpc, FTP_STOP); + if(fileName && *fileName) + ftpc->file = fileName; + else + ftpc->file = NULL; /* instead of point to a zero byte, + we make it a NULL pointer */ - return CURLE_OK; -} + if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { + /* We need a filename when uploading. Return error! */ + failf(data, "Uploading to a URL without a filename"); + return CURLE_URL_MALFORMAT; + } -static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn, - const char *line, size_t len, int *code) -{ - curl_off_t status; - (void)data; - (void)conn; + ftpc->cwddone = FALSE; /* default to not done */ - if((len > 3) && LASTLINE(line) && !curlx_str_number(&line, &status, 999)) { - *code = (int)status; - return TRUE; + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + ftpc->cwddone = TRUE; /* skip CWD for absolute paths */ + else { /* newly created FTP connections are already in entry path */ + const char *oldPath = data->conn->bits.reuse ? ftpc->prevpath : ""; + if(oldPath) { + size_t n = pathLen; + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + n = 0; /* CWD to entry for relative paths */ + else + n -= ftpc->file ? strlen(ftpc->file) : 0; + + if((strlen(oldPath) == n) && rawPath && !strncmp(rawPath, oldPath, n)) { + infof(data, "Request has same path as previous transfer"); + ftpc->cwddone = TRUE; + } + } } - return FALSE; + return CURLE_OK; } -static CURLcode ftp_readresp(struct Curl_easy *data, - struct ftp_conn *ftpc, - int sockindex, - struct pingpong *pp, - int *ftpcodep, /* return the ftp-code if done */ - size_t *size) /* size of the response */ +/*********************************************************************** + * + * ftp_need_type() + * + * Returns TRUE if we in the current situation should send TYPE + */ +static int ftp_need_type(struct ftp_conn *ftpc, + bool ascii_wanted) { - int code; - CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size); - DEBUGASSERT(ftpcodep); - - /* store the latest code for later retrieval, except during shutdown */ - if(!ftpc->shutdown) - data->info.httpcode = code; - - *ftpcodep = code; - - if(code == 421) { - /* 421 means "Service not available, closing control connection." and FTP - * servers use it to signal that idle session timeout has been exceeded. - * If we ignored the response, it could end up hanging in some cases. - * - * This response code can come at any point so having it treated - * generically is a good idea. - */ - infof(data, "We got a 421 - timeout"); - ftp_state(data, ftpc, FTP_STOP); - return CURLE_OPERATION_TIMEDOUT; - } - - return result; + return ftpc->transfertype != (ascii_wanted ? 'A' : 'I'); } -/* --- parse FTP server responses --- */ +static void close_secondarysocket(struct Curl_easy *data, + struct ftp_conn *ftpc) +{ + (void)ftpc; + CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_CSTATE(ftpc)); + Curl_conn_close(data, SECONDARYSOCKET); + Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); +} +#ifdef CURL_PREFER_LF_LINEENDS /* - * getftpresponse() is a BLOCKING function to read the full response from a - * server after a command. - * + * Lineend Conversions + * On ASCII transfers, e.g. directory listings, we might get lines + * ending in '\r\n' and we prefer '\n'. + * We might also get a lonely '\r' which we convert into a '\n'. */ -static CURLcode getftpresponse(struct Curl_easy *data, - size_t *nreadp, /* return number of bytes - read */ - int *ftpcodep) /* return the ftp-code */ -{ - /* - * We cannot read just one byte per read() and then go back to select() as - * the OpenSSL read() does not grok that properly. - * - * Alas, read as much as possible, split up into lines, use the ending - * line in a response or continue reading. */ +struct ftp_cw_lc_ctx { + struct Curl_cwriter super; + bool newline_pending; +}; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - CURLcode result = CURLE_OK; +static CURLcode ftp_cw_lc_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t blen) +{ + static const char nl = '\n'; + struct ftp_cw_lc_ctx *ctx = writer->ctx; struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - struct pingpong *pp = &ftpc->pp; - size_t nread; - int cache_skip = 0; - DEBUGASSERT(ftpcodep); - - CURL_TRC_FTP(data, "getftpresponse start"); - *nreadp = 0; - *ftpcodep = 0; /* 0 for errors */ if(!ftpc) return CURLE_FAILED_INIT; - while(!*ftpcodep && !result) { - /* check and reset timeout value every lap */ - timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE); - timediff_t interval_ms; + if(!(type & CLIENTWRITE_BODY) || ftpc->transfertype != 'A') + return Curl_cwriter_write(data, writer->next, type, buf, blen); - if(timeout <= 0) { - failf(data, "FTP response timeout"); - return CURLE_OPERATION_TIMEDOUT; /* already too little time */ - } - - interval_ms = 1000; /* use 1 second timeout intervals */ - if(timeout < interval_ms) - interval_ms = timeout; - - /* - * Since this function is blocking, we need to wait here for input on the - * connection and only then we call the response reading function. We do - * timeout at least every second to make the timeout check run. - * - * A caution here is that the ftp_readresp() function has a cache that may - * contain pieces of a response from the previous invoke and we need to - * make sure we do not just wait for input while there is unhandled data in - * that cache. But also, if the cache is there, we call ftp_readresp() and - * the cache was not good enough to continue we must not just busy-loop - * around this function. - * - */ + /* ASCII mode BODY data, convert lineends */ + while(blen) { + /* do not pass EOS when writing parts */ + int chunk_type = (type & ~CLIENTWRITE_EOS); + const char *cp; + size_t chunk_len; + CURLcode result; - if(curlx_dyn_len(&pp->recvbuf) && (cache_skip < 2)) { - /* - * There is a cache left since before. We then skipping the wait for - * socket action, unless this is the same cache like the previous round - * as then the cache was deemed not enough to act on and we then need to - * wait for more data anyway. - */ - } - else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) { - curl_socket_t wsock = Curl_pp_needs_flush(data, pp) ? - sockfd : CURL_SOCKET_BAD; - int ev = Curl_socket_check(sockfd, CURL_SOCKET_BAD, wsock, interval_ms); - if(ev < 0) { - failf(data, "FTP response aborted due to select/poll error: %d", - SOCKERRNO); - return CURLE_RECV_ERROR; - } - else if(ev == 0) { - result = Curl_pgrsUpdate(data); - continue; /* just continue in our loop for the timeout duration */ + if(ctx->newline_pending) { + if(buf[0] != '\n') { + /* previous chunk ended in '\r' and we do not see a '\n' in this one, + * need to write a newline. */ + result = Curl_cwriter_write(data, writer->next, chunk_type, &nl, 1); + if(result) + return result; } + /* either we wrote the newline or it is part of the next chunk of bytes + * we write. */ + ctx->newline_pending = FALSE; } - if(Curl_pp_needs_flush(data, pp)) { - result = Curl_pp_flushsend(data, pp); + cp = memchr(buf, '\r', blen); + if(!cp) + break; + + /* write the bytes before the '\r', excluding the '\r' */ + chunk_len = cp - buf; + if(chunk_len) { + result = Curl_cwriter_write(data, writer->next, chunk_type, + buf, chunk_len); if(result) - break; + return result; } + /* skip the '\r', we now have a newline pending */ + buf = cp + 1; + blen = blen - chunk_len - 1; + ctx->newline_pending = TRUE; + } - result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, ftpcodep, &nread); - if(result) - break; - - if(!nread && curlx_dyn_len(&pp->recvbuf)) - /* bump cache skip counter as on repeated skips we must wait for more - data */ - cache_skip++; - else - /* when we got data or there is no cache left, we reset the cache skip - counter */ - cache_skip = 0; + /* Any remaining data does not contain a '\r' */ + if(blen) { + DEBUGASSERT(!ctx->newline_pending); + return Curl_cwriter_write(data, writer->next, type, buf, blen); + } + else if(type & CLIENTWRITE_EOS) { + /* EndOfStream, if we have a trailing cr, now is the time to write it */ + if(ctx->newline_pending) { + ctx->newline_pending = FALSE; + return Curl_cwriter_write(data, writer->next, type, &nl, 1); + } + /* Always pass on the EOS type indicator */ + return Curl_cwriter_write(data, writer->next, type, buf, 0); + } + return CURLE_OK; +} - *nreadp += nread; +static const struct Curl_cwtype ftp_cw_lc = { + "ftp-lineconv", + NULL, + Curl_cwriter_def_init, + ftp_cw_lc_write, + Curl_cwriter_def_close, + sizeof(struct ftp_cw_lc_ctx) +}; - } /* while there is buffer left and loop is requested */ +#endif /* CURL_PREFER_LF_LINEENDS */ - pp->pending_resp = FALSE; - CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zd, ftpcode=%d", - result, *nreadp, *ftpcodep); +/*********************************************************************** + * + * ftp_check_ctrl_on_data_wait() + * + */ +static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data, + struct ftp_conn *ftpc) +{ + struct connectdata *conn = data->conn; + curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET]; + struct pingpong *pp = &ftpc->pp; + size_t nread; + int ftpcode; + bool response = FALSE; - return result; -} + /* First check whether there is a cached response from server */ + if(curlx_dyn_len(&pp->recvbuf)) { + const char *l = curlx_dyn_ptr(&pp->recvbuf); + if(!ISDIGIT(*l) || (*l > '3')) { + /* Data connection could not be established, let's return */ + infof(data, "There is negative response in cache while serv connect"); + (void)getftpresponse(data, &nread, &ftpcode); + return CURLE_FTP_ACCEPT_FAILED; + } + } -static CURLcode ftp_state_user(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct connectdata *conn) -{ - CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s", - conn->user ? conn->user : ""); - if(!result) { - ftpc->ftp_trying_alternative = FALSE; - ftp_state(data, ftpc, FTP_USER); + if(pp->overflow) + /* there is pending control data still in the buffer to read */ + response = TRUE; + else { + int socketstate = SOCKET_READABLE(ctrl_sock, 0); + /* see if the connection request is already here */ + switch(socketstate) { + case -1: /* error */ + /* let's die here */ + failf(data, "Error while waiting for server connect"); + return CURLE_FTP_ACCEPT_FAILED; + default: + if(socketstate & CURL_CSELECT_IN) + response = TRUE; + break; + } } - return result; -} -static CURLcode ftp_state_pwd(struct Curl_easy *data, - struct ftp_conn *ftpc) -{ - CURLcode result; -#ifdef DEBUGBUILD - if(!data->id && getenv("CURL_FTP_PWD_STOP")) - return CURLE_OK; -#endif - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD"); - if(!result) - ftp_state(data, ftpc, FTP_PWD); + if(response) { + infof(data, "Ctrl conn has data while waiting for data conn"); + if(pp->overflow > 3) { + const char *r = curlx_dyn_ptr(&pp->recvbuf); + size_t len = curlx_dyn_len(&pp->recvbuf); - return result; -} + DEBUGASSERT((pp->overflow + pp->nfinal) <= curlx_dyn_len(&pp->recvbuf)); + /* move over the most recently handled response line */ + r += pp->nfinal; + len -= pp->nfinal; -/* For the FTP "protocol connect" and "doing" phases only */ -static CURLcode ftp_pollset(struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - return ftpc ? Curl_pp_pollset(data, &ftpc->pp, ps) : CURLE_OK; -} + if((len > 3) && LASTLINE(r)) { + curl_off_t status; + if(!curlx_str_number(&r, &status, 999) && (status == 226)) { + /* funny timing situation where we get the final message on the + control connection before traffic on the data connection has been + noticed. Leave the 226 in there and use this as a trigger to read + the data socket. */ + infof(data, "Got 226 before data activity"); + return CURLE_OK; + } + } + } -/* For the FTP "DO_MORE" phase only */ -static CURLcode ftp_domore_pollset(struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + (void)getftpresponse(data, &nread, &ftpcode); - if(!ftpc) - return CURLE_OK; + infof(data, "FTP code: %03d", ftpcode); - /* When in DO_MORE state, we could be either waiting for us to connect to a - * remote site, or we could wait for that site to connect to us. Or just - * handle ordinary commands. - */ - CURL_TRC_FTP(data, "[%s] ftp_domore_pollset()", FTP_CSTATE(ftpc)); + if(ftpcode / 100 > 3) + return CURLE_FTP_ACCEPT_FAILED; - if(FTP_STOP == ftpc->state) { - /* if stopped and still in this state, then we are also waiting for a - connect on the secondary connection */ - DEBUGASSERT(data->conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD || - (data->conn->cfilter[SECONDARYSOCKET] && - !Curl_conn_is_connected(data->conn, SECONDARYSOCKET))); - /* An unconnected SECONDARY will add its socket by itself - * via its adjust_pollset() */ - return Curl_pollset_add_in(data, ps, data->conn->sock[FIRSTSOCKET]); + return CURLE_WEIRD_SERVER_REPLY; } - return Curl_pp_pollset(data, &ftpc->pp, ps); -} -static int pathlen(struct ftp_conn *ftpc, int num) -{ - DEBUGASSERT(ftpc->dirs); - DEBUGASSERT(ftpc->dirdepth > num); - return ftpc->dirs[num].len; + return CURLE_OK; } -static const char *pathpiece(struct ftp_conn *ftpc, int num) +/*********************************************************************** + * + * ftp_initiate_transfer() + * + * After connection from server is accepted this function is called to + * setup transfer parameters and initiate the data transfer. + * + */ +static CURLcode ftp_initiate_transfer(struct Curl_easy *data, + struct ftp_conn *ftpc) { - DEBUGASSERT(ftpc->dirs); - DEBUGASSERT(ftpc->dirdepth > num); - return &ftpc->rawpath[ftpc->dirs[num].start]; -} - -/* This is called after the FTP_QUOTE state is passed. + CURLcode result = CURLE_OK; + bool connected; - ftp_state_cwd() sends the range of CWD commands to the server to change to - the correct directory. It may also need to send MKD commands to create - missing ones, if that option is enabled. -*/ -static CURLcode ftp_state_cwd(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) -{ - CURLcode result = CURLE_OK; + CURL_TRC_FTP(data, "ftp_initiate_transfer()"); + result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected); + if(result || !connected) + return result; - if(ftpc->cwddone) - /* already done and fine */ - result = ftp_state_mdtm(data, ftpc, ftp); + if(data->state.upload) { + /* When we know we are uploading a specified file, we can get the file + size prior to the actual upload. */ + Curl_pgrsSetUploadSize(data, data->state.infilesize); + + /* FTP upload, shutdown DATA, ignore shutdown errors, as we rely + * on the server response on the CONTROL connection. */ + Curl_xfer_setup_send(data, SECONDARYSOCKET); + Curl_xfer_set_shutdown(data, TRUE, TRUE); + } else { - /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */ - DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) || - !(ftpc->dirdepth && ftpc->rawpath[0] == '/')); + /* FTP download, shutdown, do not ignore errors */ + Curl_xfer_setup_recv(data, SECONDARYSOCKET, data->req.size); + Curl_xfer_set_shutdown(data, TRUE, FALSE); + } - ftpc->count2 = 0; /* count2 counts failed CWDs */ + ftpc->pp.pending_resp = TRUE; /* expect server response */ + ftp_state(data, ftpc, FTP_STOP); - if(data->conn->bits.reuse && ftpc->entrypath && - /* no need to go to entrypath when we have an absolute path */ - !(ftpc->dirdepth && ftpc->rawpath[0] == '/')) { - /* This is a reused connection. Since we change directory to where the - transfer is taking place, we must first get back to the original dir - where we ended up after login: */ - ftpc->cwdcount = 0; /* we count this as the first path, then we add one - for all upcoming ones in the ftp->dirs[] array */ - result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath); - if(!result) - ftp_state(data, ftpc, FTP_CWD); - } - else { - if(ftpc->dirdepth) { - ftpc->cwdcount = 1; - /* issue the first CWD, the rest is sent when the CWD responses are - received... */ - result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s", - pathlen(ftpc, 0), pathpiece(ftpc, 0)); - if(!result) - ftp_state(data, ftpc, FTP_CWD); - } - else { - /* No CWD necessary */ - result = ftp_state_mdtm(data, ftpc, ftp); - } - } - } - return result; + return CURLE_OK; } -typedef enum { - EPRT, - PORT, - DONE -} ftpport; - -static CURLcode ftp_state_use_port(struct Curl_easy *data, - struct ftp_conn *ftpc, - ftpport fcmd) /* start with this */ +static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn, + const char *line, size_t len, int *code) { - CURLcode result = CURLE_FTP_PORT_FAILED; - struct connectdata *conn = data->conn; - curl_socket_t portsock = CURL_SOCKET_BAD; - char myhost[MAX_IPADR_LEN + 1] = ""; + curl_off_t status; + (void)data; + (void)conn; - struct Curl_sockaddr_storage ss; - struct Curl_addrinfo *res, *ai; - curl_socklen_t sslen; - char hbuf[NI_MAXHOST]; - struct sockaddr *sa = (struct sockaddr *)&ss; - struct sockaddr_in * const sa4 = (void *)sa; -#ifdef USE_IPV6 - struct sockaddr_in6 * const sa6 = (void *)sa; -#endif - static const char mode[][5] = { "EPRT", "PORT" }; - int error; - char *host = NULL; - char *string_ftpport = data->set.str[STRING_FTPPORT]; - struct Curl_dns_entry *dns_entry = NULL; - unsigned short port_min = 0; - unsigned short port_max = 0; - unsigned short port; - bool possibly_non_local = TRUE; - char buffer[STRERROR_LEN]; - char *addr = NULL; - size_t addrlen = 0; - char ipstr[50]; + if((len > 3) && LASTLINE(line) && !curlx_str_number(&line, &status, 999)) { + *code = (int)status; + return TRUE; + } - /* Step 1, figure out what is requested, - * accepted format : - * (ipv4|ipv6|domain|interface)?(:port(-range)?)? - */ + return FALSE; +} - if(data->set.str[STRING_FTPPORT] && - (strlen(data->set.str[STRING_FTPPORT]) > 1)) { - char *ip_end = NULL; +static CURLcode ftp_readresp(struct Curl_easy *data, + struct ftp_conn *ftpc, + int sockindex, + struct pingpong *pp, + int *ftpcodep, /* return the ftp-code if done */ + size_t *size) /* size of the response */ +{ + int code; + CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size); + DEBUGASSERT(ftpcodep); -#ifdef USE_IPV6 - if(*string_ftpport == '[') { - /* [ipv6]:port(-range) */ - char *ip_start = string_ftpport + 1; - ip_end = strchr(ip_start, ']'); - if(ip_end) { - addrlen = ip_end - ip_start; - addr = ip_start; - } - } - else -#endif - if(*string_ftpport == ':') { - /* :port */ - ip_end = string_ftpport; - } - else { - ip_end = strchr(string_ftpport, ':'); - addr = string_ftpport; - if(ip_end) { - /* either ipv6 or (ipv4|domain|interface):port(-range) */ - addrlen = ip_end - string_ftpport; -#ifdef USE_IPV6 - if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { - /* ipv6 */ - port_min = port_max = 0; - ip_end = NULL; /* this got no port ! */ - } -#endif - } - else - /* ipv4|interface */ - addrlen = strlen(string_ftpport); - } + /* store the latest code for later retrieval, except during shutdown */ + if(!ftpc->shutdown) + data->info.httpcode = code; - /* parse the port */ - if(ip_end) { - const char *portp = strchr(ip_end, ':'); - if(portp) { - curl_off_t start; - curl_off_t end; - portp++; - if(!curlx_str_number(&portp, &start, 0xffff)) { - /* got the first number */ - port_min = (unsigned short)start; - if(!curlx_str_single(&portp, '-')) { - /* got the dash */ - if(!curlx_str_number(&portp, &end, 0xffff)) - /* got the second number */ - port_max = (unsigned short)end; - } - } - else - port_max = port_min; - } - } + *ftpcodep = code; - /* correct errors like: - * :1234-1230 - * :-4711, in this case port_min is (unsigned)-1, - * therefore port_min > port_max for all cases - * but port_max = (unsigned)-1 + if(code == 421) { + /* 421 means "Service not available, closing control connection." and FTP + * servers use it to signal that idle session timeout has been exceeded. + * If we ignored the response, it could end up hanging in some cases. + * + * This response code can come at any point so having it treated + * generically is a good idea. */ - if(port_min > port_max) - port_min = port_max = 0; - - if(addrlen) { - const struct Curl_sockaddr_ex *remote_addr = - Curl_conn_get_remote_addr(data, FIRSTSOCKET); - - DEBUGASSERT(remote_addr); - if(!remote_addr) - goto out; - DEBUGASSERT(addr); - if(addrlen >= sizeof(ipstr)) - goto out; - memcpy(ipstr, addr, addrlen); - ipstr[addrlen] = 0; - - /* attempt to get the address of the given interface name */ - switch(Curl_if2ip(remote_addr->family, -#ifdef USE_IPV6 - Curl_ipv6_scope(&remote_addr->curl_sa_addr), - conn->scope_id, -#endif - ipstr, hbuf, sizeof(hbuf))) { - case IF2IP_NOT_FOUND: - /* not an interface, use the given string as hostname instead */ - host = ipstr; - break; - case IF2IP_AF_NOT_SUPPORTED: - goto out; - case IF2IP_FOUND: - host = hbuf; /* use the hbuf for hostname */ - break; - } - } - else - /* there was only a port(-range) given, default the host */ - host = NULL; - } /* data->set.ftpport */ - - if(!host) { - const char *r; - /* not an interface and not a hostname, get default by extracting - the IP from the control connection */ - sslen = sizeof(ss); - if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { - failf(data, "getsockname() failed: %s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; - } - switch(sa->sa_family) { -#ifdef USE_IPV6 - case AF_INET6: - r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); - break; -#endif - default: - r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); - break; - } - if(!r) { - goto out; - } - host = hbuf; /* use this hostname */ - possibly_non_local = FALSE; /* we know it is local now */ + infof(data, "We got a 421 - timeout"); + ftp_state(data, ftpc, FTP_STOP); + return CURLE_OPERATION_TIMEDOUT; } - /* resolv ip/host to ip */ - res = NULL; - result = Curl_resolv_blocking(data, host, 0, conn->ip_version, &dns_entry); - if(!result) { - DEBUGASSERT(dns_entry); - res = dns_entry->addr; - } + return result; +} - if(!res) { - failf(data, "failed to resolve the address provided to PORT: %s", host); - goto out; - } +/* --- parse FTP server responses --- */ - host = NULL; +/* + * getftpresponse() is a BLOCKING function to read the full response from a + * server after a command. + * + */ +static CURLcode getftpresponse(struct Curl_easy *data, + size_t *nreadp, /* return number of bytes + read */ + int *ftpcodep) /* return the ftp-code */ +{ + /* + * We cannot read one byte per read() and then go back to select() as the + * OpenSSL read() does not grok that properly. + * + * Alas, read as much as possible, split up into lines, use the ending + * line in a response or continue reading. */ - /* step 2, create a socket for the requested address */ - error = 0; - for(ai = res; ai; ai = ai->ai_next) { - result = Curl_socket_open(data, ai, NULL, - Curl_conn_get_transport(data, conn), &portsock); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - goto out; - result = CURLE_FTP_PORT_FAILED; - error = SOCKERRNO; - continue; - } - break; - } - if(!ai) { - failf(data, "socket failure: %s", - curlx_strerror(error, buffer, sizeof(buffer))); - goto out; - } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket", - FTP_CSTATE(ftpc)); + struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + struct pingpong *pp = &ftpc->pp; + size_t nread; + int cache_skip = 0; + DEBUGASSERT(ftpcodep); - /* step 3, bind to a suitable local address */ + CURL_TRC_FTP(data, "getftpresponse start"); + *nreadp = 0; + *ftpcodep = 0; /* 0 for errors */ - memcpy(sa, ai->ai_addr, ai->ai_addrlen); - sslen = ai->ai_addrlen; + if(!ftpc) + return CURLE_FAILED_INIT; - for(port = port_min; port <= port_max;) { - if(sa->sa_family == AF_INET) - sa4->sin_port = htons(port); -#ifdef USE_IPV6 - else - sa6->sin6_port = htons(port); -#endif - /* Try binding the given address. */ - if(bind(portsock, sa, sslen)) { - /* It failed. */ - error = SOCKERRNO; - if(possibly_non_local && (error == SOCKEADDRNOTAVAIL)) { - /* The requested bind address is not local. Use the address used for - * the control connection instead and restart the port loop - */ - infof(data, "bind(port=%hu) on non-local address failed: %s", port, - curlx_strerror(error, buffer, sizeof(buffer))); + while(!*ftpcodep && !result) { + /* check and reset timeout value every lap */ + timediff_t timeout = Curl_pp_state_timeout(data, pp); + timediff_t interval_ms; - sslen = sizeof(ss); - if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { - failf(data, "getsockname() failed: %s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; - } - port = port_min; - possibly_non_local = FALSE; /* do not try this again */ - continue; - } - if(error != SOCKEADDRINUSE && error != SOCKEACCES) { - failf(data, "bind(port=%hu) failed: %s", port, - curlx_strerror(error, buffer, sizeof(buffer))); - goto out; - } + if(timeout <= 0) { + failf(data, "FTP response timeout"); + return CURLE_OPERATION_TIMEDOUT; /* already too little time */ } - else - break; - /* check if port is the maximum value here, because it might be 0xffff and - then the increment below will wrap the 16 bit counter */ - if(port == port_max) { - /* maybe all ports were in use already */ - failf(data, "bind() failed, ran out of ports"); - goto out; - } - port++; - } + interval_ms = 1000; /* use 1 second timeout intervals */ + if(timeout < interval_ms) + interval_ms = timeout; - /* get the name again after the bind() so that we can extract the - port number it uses now */ - sslen = sizeof(ss); - if(getsockname(portsock, sa, &sslen)) { - failf(data, "getsockname() failed: %s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; - } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d", - FTP_CSTATE(ftpc), port); - - /* step 4, listen on the socket */ - - if(listen(portsock, 1)) { - failf(data, "socket failure: %s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; - } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d", - FTP_CSTATE(ftpc), port); - - /* step 5, send the proper FTP command */ - - /* get a plain printable version of the numerical address to work with - below */ - Curl_printable_address(ai, myhost, sizeof(myhost)); - -#ifdef USE_IPV6 - if(!conn->bits.ftp_use_eprt && conn->bits.ipv6) - /* EPRT is disabled but we are connected to an IPv6 host, so we ignore the - request and enable EPRT again! */ - conn->bits.ftp_use_eprt = TRUE; -#endif - - for(; fcmd != DONE; fcmd++) { - - if(!conn->bits.ftp_use_eprt && (EPRT == fcmd)) - /* if disabled, goto next */ - continue; - - if((PORT == fcmd) && sa->sa_family != AF_INET) - /* PORT is IPv4 only */ - continue; - - switch(sa->sa_family) { - case AF_INET: - port = ntohs(sa4->sin_port); - break; -#ifdef USE_IPV6 - case AF_INET6: - port = ntohs(sa6->sin6_port); - break; -#endif - default: - continue; /* might as well skip this */ - } + /* + * Since this function is blocking, we need to wait here for input on the + * connection and only then we call the response reading function. We do + * timeout at least every second to make the timeout check run. + * + * A caution here is that the ftp_readresp() function has a cache that may + * contain pieces of a response from the previous invoke and we need to + * make sure we do not wait for input while there is unhandled data in + * that cache. But also, if the cache is there, we call ftp_readresp() and + * the cache was not good enough to continue we must not busy-loop around + * this function. + * + */ - if(EPRT == fcmd) { + if(curlx_dyn_len(&pp->recvbuf) && (cache_skip < 2)) { /* - * Two fine examples from RFC2428; - * - * EPRT |1|132.235.1.2|6275| - * - * EPRT |2|1080::8:800:200C:417A|5282| + * There is a cache left since before. We then skipping the wait for + * socket action, unless this is the same cache like the previous round + * as then the cache was deemed not enough to act on and we then need to + * wait for more data anyway. */ - - result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd], - sa->sa_family == AF_INET ? 1 : 2, - myhost, port); - if(result) { - failf(data, "Failure sending EPRT command: %s", - curl_easy_strerror(result)); - goto out; - } - break; } - if(PORT == fcmd) { - /* large enough for [IP address],[num],[num] */ - char target[sizeof(myhost) + 20]; - char *source = myhost; - char *dest = target; - - /* translate x.x.x.x to x,x,x,x */ - while(*source) { - if(*source == '.') - *dest = ','; - else - *dest = *source; - dest++; - source++; + else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) { + curl_socket_t wsock = Curl_pp_needs_flush(data, pp) ? + sockfd : CURL_SOCKET_BAD; + int ev = Curl_socket_check(sockfd, CURL_SOCKET_BAD, wsock, interval_ms); + if(ev < 0) { + failf(data, "FTP response aborted due to select/poll error: %d", + SOCKERRNO); + return CURLE_RECV_ERROR; } - *dest = 0; - curl_msnprintf(dest, 20, ",%d,%d", (int)(port >> 8), (int)(port & 0xff)); - - result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target); - if(result) { - failf(data, "Failure sending PORT command: %s", - curl_easy_strerror(result)); - goto out; + else if(ev == 0) { + result = Curl_pgrsUpdate(data); + continue; /* continue in our loop for the timeout duration */ } - break; } - } - - /* store which command was sent */ - ftpc->count1 = fcmd; - ftp_state(data, ftpc, FTP_PORT); - - /* Replace any filter on SECONDARY with one listening on this socket */ - result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); - if(!result) - portsock = CURL_SOCKET_BAD; /* now held in filter */ -out: - /* If we looked up a dns_entry, now is the time to safely release it */ - if(dns_entry) - Curl_resolv_unlink(data, &dns_entry); - if(result) { - ftp_state(data, ftpc, FTP_STOP); - } - else { - /* successfully setup the list socket filter. Do we need more? */ - 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(Curl_pp_needs_flush(data, pp)) { + result = Curl_pp_flushsend(data, pp); + if(result) + break; } - conn->bits.do_more = FALSE; - Curl_pgrsTime(data, TIMER_STARTACCEPT); - Curl_expire(data, (data->set.accepttimeout > 0) ? - data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, - EXPIRE_FTP_ACCEPT); - } - if(portsock != CURL_SOCKET_BAD) - Curl_socket_close(data, conn, portsock); - return result; -} - -static CURLcode ftp_state_use_pasv(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - /* - Here's the executive summary on what to do: - - PASV is RFC959, expect: - 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) - LPSV is RFC1639, expect: - 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2) + result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, ftpcodep, &nread); + if(result) + break; - EPSV is RFC2428, expect: - 229 Entering Extended Passive Mode (|||port|) + if(!nread && curlx_dyn_len(&pp->recvbuf)) + /* bump cache skip counter as on repeated skips we must wait for more + data */ + cache_skip++; + else + /* when we got data or there is no cache left, we reset the cache skip + counter */ + cache_skip = 0; - */ + *nreadp += nread; - static const char mode[][5] = { "EPSV", "PASV" }; - int modeoff; + } /* while there is buffer left and loop is requested */ -#ifdef PF_INET6 - if(!conn->bits.ftp_use_epsv && conn->bits.ipv6) - /* EPSV is disabled but we are connected to an IPv6 host, so we ignore the - request and enable EPSV again! */ - conn->bits.ftp_use_epsv = TRUE; -#endif + pp->pending_resp = FALSE; + CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zd, ftpcode=%d", + result, *nreadp, *ftpcodep); - modeoff = conn->bits.ftp_use_epsv ? 0 : 1; + return result; +} - result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]); +static CURLcode ftp_state_user(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct connectdata *conn) +{ + CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s", + conn->user ? conn->user : ""); if(!result) { - ftpc->count1 = modeoff; - ftp_state(data, ftpc, FTP_PASV); - infof(data, "Connect data stream passively"); + ftpc->ftp_trying_alternative = FALSE; + ftp_state(data, ftpc, FTP_USER); } return result; } -/* - * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc. - * - * REST is the last command in the chain of commands when a "head"-like - * request is made. Thus, if an actual transfer is to be made this is where we - * take off for real. - */ -static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) +static CURLcode ftp_state_pwd(struct Curl_easy *data, + struct ftp_conn *ftpc) { - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - if(ftp->transfer != PPTRANSFER_BODY) { - /* does not transfer any data */ + CURLcode result; +#ifdef DEBUGBUILD + if(!data->id && getenv("CURL_FTP_PWD_STOP")) + return CURLE_OK; +#endif + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD"); + if(!result) + ftp_state(data, ftpc, FTP_PWD); - /* still possibly do PRE QUOTE jobs */ - ftp_state(data, ftpc, FTP_RETR_PREQUOTE); - result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE); - } - else if(data->set.ftp_use_port) { - /* We have chosen to use the PORT (or similar) command */ - result = ftp_state_use_port(data, ftpc, EPRT); - } - else { - /* We have chosen (this is default) to use the PASV (or similar) command */ - if(data->set.ftp_use_pret) { - /* The user has requested that we send a PRET command - to prepare the server for the upcoming PASV */ - if(!ftpc->file) - result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s", - data->set.str[STRING_CUSTOMREQUEST] ? - data->set.str[STRING_CUSTOMREQUEST] : - (data->state.list_only ? "NLST" : "LIST")); - else if(data->state.upload) - result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", ftpc->file); - else - result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_PRET); - } - else - result = ftp_state_use_pasv(data, ftpc, conn); - } return result; } -static CURLcode ftp_state_rest(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) +/* For the FTP "protocol connect" and "doing" phases only */ +static CURLcode ftp_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + return ftpc ? Curl_pp_pollset(data, &ftpc->pp, ps) : CURLE_OK; +} - if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) { - /* if a "head"-like request is being made (on a file) */ +/* For the FTP "DO_MORE" phase only */ +static CURLcode ftp_domore_pollset(struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - /* Determine if server can respond to REST command and therefore - whether it supports range */ - result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0); - if(!result) - ftp_state(data, ftpc, FTP_REST); + if(!ftpc) + return CURLE_OK; + + /* When in DO_MORE state, we could be either waiting for us to connect to a + * remote site, or we could wait for that site to connect to us. Or handle + * ordinary commands. + */ + CURL_TRC_FTP(data, "[%s] ftp_domore_pollset()", FTP_CSTATE(ftpc)); + + if(FTP_STOP == ftpc->state) { + /* if stopped and still in this state, then we are also waiting for a + connect on the secondary connection */ + DEBUGASSERT(data->conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD || + (data->conn->cfilter[SECONDARYSOCKET] && + !Curl_conn_is_connected(data->conn, SECONDARYSOCKET))); + /* An unconnected SECONDARY will add its socket by itself + * via its adjust_pollset() */ + return Curl_pollset_add_in(data, ps, data->conn->sock[FIRSTSOCKET]); } - else - result = ftp_state_prepare_transfer(data, ftpc, ftp); + return Curl_pp_pollset(data, &ftpc->pp, ps); +} - return result; +static int pathlen(struct ftp_conn *ftpc, int num) +{ + DEBUGASSERT(ftpc->dirs); + DEBUGASSERT(ftpc->dirdepth > num); + return ftpc->dirs[num].len; } -static CURLcode ftp_state_size(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) +static const char *pathpiece(struct ftp_conn *ftpc, int num) +{ + DEBUGASSERT(ftpc->dirs); + DEBUGASSERT(ftpc->dirdepth > num); + return &ftpc->rawpath[ftpc->dirs[num].start]; +} + +/* This is called after the FTP_QUOTE state is passed. + + ftp_state_cwd() sends the range of CWD commands to the server to change to + the correct directory. It may also need to send MKD commands to create + missing ones, if that option is enabled. +*/ +static CURLcode ftp_state_cwd(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) { CURLcode result = CURLE_OK; - if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) { - /* if a "head"-like request is being made (on a file) */ + if(ftpc->cwddone) + /* already done and fine */ + result = ftp_state_mdtm(data, ftpc, ftp); + else { + /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */ + DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) || + !(ftpc->dirdepth && ftpc->rawpath[0] == '/')); - /* we know ftpc->file is a valid pointer to a filename */ - result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_SIZE); - } - else - result = ftp_state_rest(data, ftpc, ftp); + ftpc->count2 = 0; /* count2 counts failed CWDs */ + if(data->conn->bits.reuse && ftpc->entrypath && + /* no need to go to entrypath when we have an absolute path */ + !(ftpc->dirdepth && ftpc->rawpath[0] == '/')) { + /* This is a reused connection. Since we change directory to where the + transfer is taking place, we must first get back to the original dir + where we ended up after login: */ + ftpc->cwdcount = 0; /* we count this as the first path, then we add one + for all upcoming ones in the ftp->dirs[] array */ + result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath); + if(!result) + ftp_state(data, ftpc, FTP_CWD); + } + else { + if(ftpc->dirdepth) { + ftpc->cwdcount = 1; + /* issue the first CWD, the rest is sent when the CWD responses are + received... */ + result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s", + pathlen(ftpc, 0), pathpiece(ftpc, 0)); + if(!result) + ftp_state(data, ftpc, FTP_CWD); + } + else { + /* No CWD necessary */ + result = ftp_state_mdtm(data, ftpc, ftp); + } + } + } return result; } -static CURLcode ftp_state_list(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) -{ - CURLcode result = CURLE_OK; +typedef enum { + EPRT, + PORT, + DONE +} ftpport; - /* If this output is to be machine-parsed, the NLST command might be better - to use, since the LIST command output is not specified or standard in any - way. It has turned out that the NLST list output is not the same on all - servers either... */ +/* + * Parse the CURLOPT_FTPPORT string + * "(ipv4|ipv6|domain|interface)?(:port(-range)?)?" + * and extract addr/addrlen and port_min/port_max. + */ +static CURLcode ftp_port_parse_string(struct Curl_easy *data, + struct connectdata *conn, + const char *string_ftpport, + struct Curl_sockaddr_storage *ss, + unsigned short *port_minp, + unsigned short *port_maxp, + const char **hostp, + char *hbuf, size_t hbuflen) +{ + const char *ip_end = NULL; + const char *addr = NULL; + size_t addrlen = 0; + unsigned short port_min = 0; + unsigned short port_max = 0; + char ipstr[50]; +#ifndef USE_IPV6 + (void)conn; + (void)ss; +#endif - /* - if FTPFILE_NOCWD was specified, we should add the path - as argument for the LIST / NLST / or custom command. - Whether the server will support this, is uncertain. + /* default to nothing */ + *hostp = NULL; + *port_minp = *port_maxp = 0; - The other ftp_filemethods will CWD into dir/dir/ first and - then just do LIST (in that case: nothing to do here) - */ - const char *lstArg = NULL; - int lstArglen = 0; - char *cmd; + if(!string_ftpport || (strlen(string_ftpport) <= 1)) + goto done; - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { - /* url-decode before evaluation: e.g. paths starting/ending with %2f */ - const char *rawPath = ftpc->rawpath; - const char *slashPos = strrchr(rawPath, '/'); - if(slashPos) { - /* chop off the file part if format is dir/file otherwise remove - the trailing slash for dir/dir/ except for absolute path / */ - size_t n = slashPos - rawPath; - if(n == 0) - ++n; +#ifdef USE_IPV6 + if(*string_ftpport == '[') { + /* [ipv6]:port(-range) */ + const char *ip_start = string_ftpport + 1; + ip_end = strchr(ip_start, ']'); + if(ip_end) { + addrlen = ip_end - ip_start; + addr = ip_start; + } + } + else +#endif + if(*string_ftpport == ':') { + /* :port */ + ip_end = string_ftpport; + } + else { + ip_end = strchr(string_ftpport, ':'); + addr = string_ftpport; + if(ip_end) { +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)ss; +#endif + /* either ipv6 or (ipv4|domain|interface):port(-range) */ + addrlen = ip_end - string_ftpport; +#ifdef USE_IPV6 + if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { + /* ipv6 */ + addrlen = strlen(string_ftpport); + ip_end = NULL; /* this got no port ! */ + } +#endif + } + else + /* ipv4|interface */ + addrlen = strlen(string_ftpport); + } - lstArg = rawPath; - lstArglen = (int)n; + /* parse the port */ + if(ip_end) { + const char *portp = strchr(ip_end, ':'); + if(portp) { + curl_off_t start; + curl_off_t end; + portp++; + if(!curlx_str_number(&portp, &start, 0xffff)) { + port_min = (unsigned short)start; + if(!curlx_str_single(&portp, '-') && + !curlx_str_number(&portp, &end, 0xffff)) + port_max = (unsigned short)end; + else + port_max = port_min; + } } } - cmd = curl_maprintf("%s%s%.*s", - data->set.str[STRING_CUSTOMREQUEST] ? - data->set.str[STRING_CUSTOMREQUEST] : - (data->state.list_only ? "NLST" : "LIST"), - lstArg ? " " : "", - lstArglen, lstArg ? lstArg : ""); + /* correct errors like :1234-1230 or :-4711 */ + if(port_min > port_max) + port_min = port_max = 0; - if(!cmd) - return CURLE_OUT_OF_MEMORY; + if(addrlen) { + const struct Curl_sockaddr_ex *remote_addr = + Curl_conn_get_remote_addr(data, FIRSTSOCKET); - result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); - curlx_free(cmd); + DEBUGASSERT(remote_addr); + DEBUGASSERT(addr); + if(!remote_addr || (addrlen >= sizeof(ipstr)) || (addrlen >= hbuflen)) + return CURLE_FTP_PORT_FAILED; + memcpy(ipstr, addr, addrlen); + ipstr[addrlen] = 0; - if(!result) - ftp_state(data, ftpc, FTP_LIST); + /* attempt to get the address of the given interface name */ + switch(Curl_if2ip(remote_addr->family, +#ifdef USE_IPV6 + Curl_ipv6_scope(&remote_addr->curl_sa_addr), + conn->scope_id, +#endif + ipstr, hbuf, hbuflen)) { + case IF2IP_NOT_FOUND: + /* not an interface, use the string as hostname instead */ + memcpy(hbuf, addr, addrlen); + hbuf[addrlen] = 0; + *hostp = hbuf; + break; + case IF2IP_AF_NOT_SUPPORTED: + return CURLE_FTP_PORT_FAILED; + case IF2IP_FOUND: + *hostp = hbuf; /* use the hbuf for hostname */ + break; + } + } + /* else: only a port(-range) given, leave host as NULL */ - return result; +done: + *port_minp = port_min; + *port_maxp = port_max; + return CURLE_OK; } -static CURLcode ftp_state_list_prequote(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) +/* + * If no host was derived from the FTPPORT string, fall back to the IP address + * of the control connection's local socket. + */ +static CURLcode ftp_port_default_host(struct Curl_easy *data, + struct connectdata *conn, + struct Curl_sockaddr_storage *ss, + curl_socklen_t *sslenp, + const char **hostp, + char *hbuf, size_t hbuflen, + bool *non_localp) { - /* We have sent the TYPE, now we must send the list of prequote strings */ - return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_LIST_PREQUOTE); -} + struct sockaddr *sa = (struct sockaddr *)ss; + struct sockaddr_in * const sa4 = (void *)sa; +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)sa; +#endif + char buffer[STRERROR_LEN]; + const char *r; -static CURLcode ftp_state_retr_prequote(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) -{ - /* We have sent the TYPE, now we must send the list of prequote strings */ - return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE); -} + *sslenp = sizeof(*ss); + if(getsockname(conn->sock[FIRSTSOCKET], sa, sslenp)) { + failf(data, "getsockname() failed: %s", + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; + } + switch(sa->sa_family) { +#ifdef USE_IPV6 + case AF_INET6: + r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, hbuflen); + break; +#endif + default: + r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, hbuflen); + break; + } + if(!r) + return CURLE_FTP_PORT_FAILED; -static CURLcode ftp_state_stor_prequote(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) -{ - /* We have sent the TYPE, now we must send the list of prequote strings */ - return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_STOR_PREQUOTE); + *hostp = hbuf; + *non_localp = FALSE; /* we know it is local now */ + return CURLE_OK; } -static CURLcode ftp_state_type(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) +/* + * Resolve the host string to a list of addresses. + */ +static CURLcode ftp_port_resolve_host(struct Curl_easy *data, + struct connectdata *conn, + const char *host, + struct Curl_dns_entry **dns_entryp, + const struct Curl_addrinfo **resp) { - CURLcode result = CURLE_OK; - - /* If we have selected NOBODY and HEADER, it means that we only want file - information. Which in FTP cannot be much more than the file size and - date. */ - if(data->req.no_body && ftpc->file && - ftp_need_type(ftpc, data->state.prefer_ascii)) { - /* The SIZE command is _not_ RFC 959 specified, and therefore many servers - may not support it! It is however the only way we have to get a file's - size! */ - - ftp->transfer = PPTRANSFER_INFO; - /* this means no actual transfer will be made */ + CURLcode result; - /* Some servers return different sizes for different modes, and thus we - must set the proper type before we check the size */ - result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, FTP_TYPE); - if(result) - return result; + *resp = NULL; + result = Curl_resolv_blocking(data, host, 0, conn->ip_version, + dns_entryp); + if(result) + failf(data, "failed to resolve the address provided to PORT: %s", host); + else { + DEBUGASSERT(*dns_entryp); + *resp = (*dns_entryp)->addr; } - else - result = ftp_state_size(data, ftpc, ftp); - return result; } -/* This is called after the CWD commands have been done in the beginning of - the DO phase */ -static CURLcode ftp_state_mdtm(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) +/* + * Open a TCP socket for the resolved address family. + */ +static CURLcode ftp_port_open_socket(struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *res, + const struct Curl_addrinfo **aip, + curl_socket_t *portsockp) { - CURLcode result = CURLE_OK; + char buffer[STRERROR_LEN]; + int error = 0; + const struct Curl_addrinfo *ai; + CURLcode result = CURLE_FTP_PORT_FAILED; - /* Requested time of file or time-depended transfer? */ - if((data->set.get_filetime || data->set.timecondition) && ftpc->file) { - - /* we have requested to get the modified-time of the file, this is a white - spot as the MDTM is not mentioned in RFC959 */ - result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file); - - if(!result) - ftp_state(data, ftpc, FTP_MDTM); + for(ai = res; ai; ai = ai->ai_next) { + result = + Curl_socket_open(data, ai, NULL, + Curl_conn_get_transport(data, conn), portsockp); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + return result; + result = CURLE_FTP_PORT_FAILED; + error = SOCKERRNO; + continue; + } + break; } - else - result = ftp_state_type(data, ftpc, ftp); - + if(!ai) { + failf(data, "socket failure: %s", + curlx_strerror(error, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; + } + *aip = ai; return result; } -/* This is called after the TYPE and possible quote commands have been sent */ -static CURLcode ftp_state_ul_setup(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool sizechecked) +/* + * Bind the socket to a local address and port within the requested range. + * Falls back to the control-connection address if the user-requested address + * is non-local. + */ +static CURLcode ftp_port_bind_socket(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t portsock, + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_storage *ss, + curl_socklen_t *sslen_io, + unsigned short port_min, + unsigned short port_max, + bool non_local) { - CURLcode result = CURLE_OK; - bool append = data->set.remote_append; + struct sockaddr *sa = (struct sockaddr *)ss; + struct sockaddr_in * const sa4 = (void *)sa; +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)sa; +#endif + char buffer[STRERROR_LEN]; + unsigned short port; + int error; - if((data->state.resume_from && !sizechecked) || - ((data->state.resume_from > 0) && sizechecked)) { - /* we are about to continue the uploading of a file */ - /* 1. get already existing file's size. We use the SIZE command for this - which may not exist in the server! The SIZE command is not in - RFC959. */ + memcpy(sa, ai->ai_addr, ai->ai_addrlen); + *sslen_io = ai->ai_addrlen; - /* 2. This used to set REST. But since we can do append, we - do not another ftp command. We just skip the source file - offset and then we APPEND the rest on the file instead */ + for(port = port_min; port <= port_max;) { + if(sa->sa_family == AF_INET) + sa4->sin_port = htons(port); +#ifdef USE_IPV6 + else + sa6->sin6_port = htons(port); +#endif + if(bind(portsock, sa, *sslen_io)) { + error = SOCKERRNO; + if(non_local && (error == SOCKEADDRNOTAVAIL)) { + /* The requested bind address is not local. Use the address used for + * the control connection instead and restart the port loop. + */ + infof(data, "bind(port=%hu) on non-local address failed: %s", port, + curlx_strerror(error, buffer, sizeof(buffer))); - /* 3. pass file-size number of bytes in the source file */ - /* 4. lower the infilesize counter */ - /* => transfer as usual */ - int seekerr = CURL_SEEKFUNC_OK; + *sslen_io = sizeof(*ss); + if(getsockname(conn->sock[FIRSTSOCKET], sa, sslen_io)) { + failf(data, "getsockname() failed: %s", + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; + } + port = port_min; + non_local = FALSE; /* do not try this again */ + continue; + } + if(error != SOCKEADDRINUSE && error != SOCKEACCES) { + failf(data, "bind(port=%hu) failed: %s", port, + curlx_strerror(error, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; + } + } + else + break; - if(data->state.resume_from < 0) { - /* Got no given size to start from, figure it out */ - result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_STOR_SIZE); - return result; + /* check if port is the maximum value here, because it might be 0xffff + and then the increment below will wrap the 16-bit counter */ + if(port == port_max) { + failf(data, "bind() failed, ran out of ports"); + return CURLE_FTP_PORT_FAILED; } + port++; + } - /* enable append */ - append = TRUE; + /* re-read the name so we can extract the actual port chosen */ + *sslen_io = sizeof(*ss); + if(getsockname(portsock, sa, sslen_io)) { + failf(data, "getsockname() failed: %s", + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; + } + CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", + port); + return CURLE_OK; +} - /* Let's read off the proper amount of bytes from the input. */ - if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); - seekerr = data->set.seek_func(data->set.seek_client, - data->state.resume_from, SEEK_SET); - Curl_set_in_callback(data, FALSE); - } +/* + * Start listening on the data socket. + */ +static CURLcode ftp_port_listen(struct Curl_easy *data, curl_socket_t portsock) +{ + char buffer[STRERROR_LEN]; - if(seekerr != CURL_SEEKFUNC_OK) { - curl_off_t passed = 0; - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_FTP_COULDNT_USE_REST; - } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ - do { - char scratch[4 * 1024]; - size_t readthisamountnow = - (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ? - sizeof(scratch) : - curlx_sotouz(data->state.resume_from - passed); + if(listen(portsock, 1)) { + failf(data, "socket failure: %s", + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; + } + CURL_TRC_FTP(data, "ftp_port_listen(), listening on port"); + return CURLE_OK; +} - size_t actuallyread = - data->state.fread_func(scratch, 1, readthisamountnow, - data->state.in); +/* + * Send the EPRT or PORT command to the server. + */ +static CURLcode ftp_port_send_command(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct connectdata *conn, + struct Curl_sockaddr_storage *ss, + const struct Curl_addrinfo *ai, + ftpport fcmd) +{ + static const char mode[][5] = { "EPRT", "PORT" }; + struct sockaddr *sa = (struct sockaddr *)ss; + struct sockaddr_in * const sa4 = (void *)sa; +#ifdef USE_IPV6 + struct sockaddr_in6 * const sa6 = (void *)sa; +#endif + char myhost[MAX_IPADR_LEN + 1] = ""; + unsigned short port; + CURLcode result; - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); - return CURLE_FTP_COULDNT_USE_REST; - } - } while(passed < data->state.resume_from); - } - /* now, decrease the size of the read */ - if(data->state.infilesize > 0) { - data->state.infilesize -= data->state.resume_from; + /* Get a plain printable version of the numerical address to work with. This + logic uses the address provided by the FTPPORT option, which at times + might differ from the address in 'ss' used to bind to: when a user asks + the server to connect to a specific address knowing that it works, but + curl instead selects to listen to the local address because it cannot use + the provided address. FTP is strange. */ + Curl_printable_address(ai, myhost, sizeof(myhost)); - if(data->state.infilesize <= 0) { - infof(data, "File already completely uploaded"); +#ifdef USE_IPV6 + if(!conn->bits.ftp_use_eprt && conn->bits.ipv6) + /* EPRT is disabled but we are connected to an IPv6 host, so we ignore the + request and enable EPRT again! */ + conn->bits.ftp_use_eprt = TRUE; +#endif - /* no data to transfer */ - Curl_xfer_setup_nop(data); + for(; fcmd != DONE; fcmd++) { - /* Set ->transfer so that we will not get any error in - * ftp_done() because we did not transfer anything! */ - ftp->transfer = PPTRANSFER_NONE; + if(!conn->bits.ftp_use_eprt && (EPRT == fcmd)) + /* if disabled, goto next */ + continue; - ftp_state(data, ftpc, FTP_STOP); - return CURLE_OK; + if((PORT == fcmd) && sa->sa_family != AF_INET) + /* PORT is IPv4 only */ + continue; + + switch(sa->sa_family) { + case AF_INET: + port = ntohs(sa4->sin_port); + break; +#ifdef USE_IPV6 + case AF_INET6: + port = ntohs(sa6->sin6_port); + break; +#endif + default: + continue; /* might as well skip this */ + } + + if(EPRT == fcmd) { + /* + * Two fine examples from RFC2428; + * + * EPRT |1|132.235.1.2|6275| + * + * EPRT |2|1080::8:800:200C:417A|5282| + */ + result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd], + sa->sa_family == AF_INET ? 1 : 2, + myhost, port); + if(result) { + failf(data, "Failure sending EPRT command: %s", + curl_easy_strerror(result)); + return result; } + break; } - /* we have passed, proceed as normal */ - } /* resume_from */ + if(PORT == fcmd) { + /* large enough for [IP address],[num],[num] */ + char target[sizeof(myhost) + 20]; + const char *source = myhost; + char *dest = target; - result = Curl_pp_sendf(data, &ftpc->pp, append ? "APPE %s" : "STOR %s", - ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_STOR); + /* translate x.x.x.x to x,x,x,x */ + while(*source) { + if(*source == '.') + *dest = ','; + else + *dest = *source; + dest++; + source++; + } + *dest = 0; + curl_msnprintf(dest, 20, ",%d,%d", (int)(port >> 8), (int)(port & 0xff)); - return result; + result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target); + if(result) { + failf(data, "Failure sending PORT command: %s", + curl_easy_strerror(result)); + return result; + } + break; + } + } + + /* store which command was sent */ + ftpc->count1 = fcmd; + ftp_state(data, ftpc, FTP_PORT); + return CURLE_OK; } -static CURLcode ftp_state_quote(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool init, - ftpstate instate) +/* + * ftp_state_use_port() + * + * Set up an active-mode FTP data connection (using PORT or EPRT) and start + * listening for the server's incoming connection on SECONDARYSOCKET. + */ +static CURLcode ftp_state_use_port(struct Curl_easy *data, + struct ftp_conn *ftpc, + ftpport fcmd) /* start with this */ { - CURLcode result = CURLE_OK; - bool quote = FALSE; - struct curl_slist *item; + CURLcode result = CURLE_FTP_PORT_FAILED; + struct connectdata *conn = data->conn; + curl_socket_t portsock = CURL_SOCKET_BAD; - switch(instate) { - case FTP_QUOTE: - default: - item = data->set.quote; - break; - case FTP_RETR_PREQUOTE: - case FTP_STOR_PREQUOTE: - case FTP_LIST_PREQUOTE: - item = data->set.prequote; - break; - case FTP_POSTQUOTE: - item = data->set.postquote; - break; - } + struct Curl_sockaddr_storage ss; + curl_socklen_t sslen; + char hbuf[NI_MAXHOST]; + const char *host = NULL; + const char *string_ftpport = data->set.str[STRING_FTPPORT]; + struct Curl_dns_entry *dns_entry = NULL; + const struct Curl_addrinfo *res = NULL; + const struct Curl_addrinfo *ai = NULL; + unsigned short port_min = 0; + unsigned short port_max = 0; + bool non_local = TRUE; + + /* parse the FTPPORT string for address and port range */ + result = ftp_port_parse_string(data, conn, string_ftpport, + &ss, &port_min, &port_max, + &host, hbuf, sizeof(hbuf)); + if(!result && !host) + /* if no host was specified, use the control connection's local IP */ + result = ftp_port_default_host(data, conn, &ss, &sslen, &host, + hbuf, sizeof(hbuf), &non_local); + + /* resolve host string to address list */ + if(!result) + result = ftp_port_resolve_host(data, conn, host, &dns_entry, &res); - /* - * This state uses: - * 'count1' to iterate over the commands to send - * 'count2' to store whether to allow commands to fail - */ + /* Open a TCP socket for the data connection */ + if(!result) + result = ftp_port_open_socket(data, conn, res, &ai, &portsock); + if(!result) { + CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket", + FTP_CSTATE(ftpc)); - if(init) - ftpc->count1 = 0; - else - ftpc->count1++; + /* bind to a suitable local address / port */ + result = ftp_port_bind_socket(data, conn, portsock, ai, &ss, &sslen, + port_min, port_max, non_local); + } - if(item) { - int i = 0; + /* listen */ + if(!result) + result = ftp_port_listen(data, portsock); - /* Skip count1 items in the linked list */ - while((i < ftpc->count1) && item) { - item = item->next; - i++; - } - if(item) { - char *cmd = item->data; - if(cmd[0] == '*') { - cmd++; - ftpc->count2 = 1; /* the sent command is allowed to fail */ - } - else - ftpc->count2 = 0; /* failure means cancel operation */ + /* send the PORT / EPRT command */ + if(!result) + result = ftp_port_send_command(data, ftpc, conn, &ss, ai, fcmd); - result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); - if(result) - return result; - ftp_state(data, ftpc, instate); - quote = TRUE; - } - } + /* replace any filter on SECONDARY with one listening on this socket */ + if(!result) + result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); - if(!quote) { - /* No more quote to send, continue to ... */ - switch(instate) { - case FTP_QUOTE: - default: - result = ftp_state_cwd(data, ftpc, ftp); - break; - case FTP_RETR_PREQUOTE: - if(ftp->transfer != PPTRANSFER_BODY) - ftp_state(data, ftpc, FTP_STOP); - else { - if(ftpc->known_filesize != -1) { - Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); - result = ftp_state_retr(data, ftpc, ftp, ftpc->known_filesize); - } - else { - if(data->set.ignorecl || data->state.prefer_ascii) { - /* 'ignorecl' is used to support download of growing files. It - prevents the state machine from requesting the file size from - the server. With an unknown file size the download continues - until the server terminates it, otherwise the client stops if - the received byte count exceeds the reported file size. Set - option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this - behavior. + if(!result) + portsock = CURL_SOCKET_BAD; /* now held in filter */ - In addition: asking for the size for 'TYPE A' transfers is not - constructive since servers do not report the converted size. So - skip it. - */ - result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_RETR); - } - else { - result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_RETR_SIZE); - } - } - } - break; - case FTP_STOR_PREQUOTE: - result = ftp_state_ul_setup(data, ftpc, ftp, FALSE); - break; - case FTP_POSTQUOTE: - break; - case FTP_LIST_PREQUOTE: - ftp_state(data, ftpc, FTP_LIST_TYPE); - result = ftp_state_list(data, ftpc, ftp); - break; + /* cleanup */ + + if(dns_entry) + Curl_resolv_unlink(data, &dns_entry); + if(result) { + ftp_state(data, ftpc, FTP_STOP); + } + else { + /* successfully set up the listen socket filter. SSL needed? */ + 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); } + conn->bits.do_more = FALSE; + Curl_pgrsTime(data, TIMER_STARTACCEPT); + Curl_expire(data, (data->set.accepttimeout > 0) ? + data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, + EXPIRE_FTP_ACCEPT); } - + if(portsock != CURL_SOCKET_BAD) + Curl_socket_close(data, conn, portsock); return result; } -/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV - problems */ -static CURLcode ftp_epsv_disable(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct connectdata *conn) +static CURLcode ftp_state_use_pasv(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct connectdata *conn) { CURLcode result = CURLE_OK; + /* + Here's the executive summary on what to do: - if(conn->bits.ipv6 -#ifndef CURL_DISABLE_PROXY - && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) + PASV is RFC959, expect: + 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) + + LPSV is RFC1639, expect: + 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2) + + EPSV is RFC2428, expect: + 229 Entering Extended Passive Mode (|||port|) + + */ + + static const char mode[][5] = { "EPSV", "PASV" }; + int modeoff; + +#ifdef PF_INET6 + if(!conn->bits.ftp_use_epsv && conn->bits.ipv6) + /* EPSV is disabled but we are connected to an IPv6 host, so we ignore the + request and enable EPSV again! */ + conn->bits.ftp_use_epsv = TRUE; #endif - ) { - /* We cannot disable EPSV when doing IPv6, so this is instead a fail */ - failf(data, "Failed EPSV attempt, exiting"); - return CURLE_WEIRD_SERVER_REPLY; - } - infof(data, "Failed EPSV attempt. Disabling EPSV"); - /* disable it for next transfer */ - conn->bits.ftp_use_epsv = FALSE; - close_secondarysocket(data, ftpc); - data->state.errorbuf = FALSE; /* allow error message to get - rewritten */ - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PASV"); + modeoff = conn->bits.ftp_use_epsv ? 0 : 1; + + result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]); if(!result) { - ftpc->count1++; - /* remain in/go to the FTP_PASV state */ + ftpc->count1 = modeoff; ftp_state(data, ftpc, FTP_PASV); + infof(data, "Connect data stream passively"); } return result; } -static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp) +/* + * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc. + * + * REST is the last command in the chain of commands when a "head"-like + * request is made. Thus, if an actual transfer is to be made this is where we + * take off for real. + */ +static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) { + CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - struct ip_quadruple ipquad; - bool is_ipv6; - /* Returns the control connection IP address. - If a proxy tunnel is used, returns the original hostname instead, because - the effective control connection address is the proxy address, - not the ftp host. */ -#ifndef CURL_DISABLE_PROXY - if(conn->bits.tunnel_proxy || conn->bits.socksproxy) - *newhostp = curlx_strdup(conn->host.name); - else -#endif - if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) && - *ipquad.remote_ip) - *newhostp = curlx_strdup(ipquad.remote_ip); - else { - /* failed to get the remote_ip of the DATA connection */ - failf(data, "unable to get peername of DATA connection"); - *newhostp = NULL; - return CURLE_FTP_CANT_GET_HOST; - } - return *newhostp ? CURLE_OK : CURLE_OUT_OF_MEMORY; -} + if(ftp->transfer != PPTRANSFER_BODY) { + /* does not transfer any data */ -static bool match_pasv_6nums(const char *p, - unsigned int *array) /* 6 numbers */ -{ - int i; - for(i = 0; i < 6; i++) { - curl_off_t num; - if(i) { - if(*p != ',') - return FALSE; - p++; + /* still possibly do PRE QUOTE jobs */ + ftp_state(data, ftpc, FTP_RETR_PREQUOTE); + result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE); + } + else if(data->set.ftp_use_port) { + /* We have chosen to use the PORT (or similar) command */ + result = ftp_state_use_port(data, ftpc, EPRT); + } + else { + /* We have chosen (this is default) to use the PASV (or similar) command */ + if(data->set.ftp_use_pret) { + /* The user has requested that we send a PRET command + to prepare the server for the upcoming PASV */ + if(!ftpc->file) + result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s", + data->set.str[STRING_CUSTOMREQUEST] ? + data->set.str[STRING_CUSTOMREQUEST] : + (data->state.list_only ? "NLST" : "LIST")); + else if(data->state.upload) + result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", ftpc->file); + else + result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_PRET); } - if(curlx_str_number(&p, &num, 0xff)) - return FALSE; - array[i] = (unsigned int)num; + else + result = ftp_state_use_pasv(data, ftpc, conn); } - return TRUE; + return result; } -static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - int ftpcode) +static CURLcode ftp_state_rest(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) { - struct connectdata *conn = data->conn; - CURLcode result; - struct Curl_dns_entry *dns = NULL; - unsigned short connectport; /* the local port connect() should use! */ - struct pingpong *pp = &ftpc->pp; - char *newhost = NULL; - unsigned short newport = 0; - char *str = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */ + CURLcode result = CURLE_OK; - if((ftpc->count1 == 0) && - (ftpcode == 229)) { - /* positive EPSV response */ - char *ptr = strchr(str, '('); - if(ptr) { - char sep; - ptr++; - /* |||12345| */ - sep = ptr[0]; - if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { - const char *p = &ptr[3]; - curl_off_t num; - if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) { - failf(data, "Illegal port number in EPSV reply"); - return CURLE_FTP_WEIRD_PASV_REPLY; - } - newport = (unsigned short)num; - result = ftp_control_addr_dup(data, &newhost); - if(result) - return result; - } - else - ptr = NULL; - } - if(!ptr) { - failf(data, "Weirdly formatted EPSV reply"); - return CURLE_FTP_WEIRD_PASV_REPLY; - } - } - else if((ftpc->count1 == 1) && - (ftpcode == 227)) { - /* positive PASV response */ - unsigned int ip[6]; + if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) { + /* if a "head"-like request is being made (on a file) */ - /* - * Scan for a sequence of six comma-separated numbers and use them as - * IP+port indicators. - * - * Found reply-strings include: - * "227 Entering Passive Mode (127,0,0,1,4,51)" - * "227 Data transfer will passively listen to 127,0,0,1,4,51" - * "227 Entering passive mode. 127,0,0,1,4,51" - */ - while(*str) { - if(match_pasv_6nums(str, ip)) - break; - str++; - } + /* Determine if server can respond to REST command and therefore + whether it supports range */ + result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0); + if(!result) + ftp_state(data, ftpc, FTP_REST); + } + else + result = ftp_state_prepare_transfer(data, ftpc, ftp); - if(!*str) { - failf(data, "Could not interpret the 227-response"); - return CURLE_FTP_WEIRD_227_FORMAT; - } + return result; +} - /* we got OK from server */ - if(data->set.ftp_skip_ip) { - /* told to ignore the remotely given IP but instead use the host we used - for the control connection */ - infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead", - ip[0], ip[1], ip[2], ip[3], conn->host.name); - result = ftp_control_addr_dup(data, &newhost); - if(result) - return result; - } - else - newhost = curl_maprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); +static CURLcode ftp_state_size(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + CURLcode result = CURLE_OK; - if(!newhost) - return CURLE_OUT_OF_MEMORY; + if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) { + /* if a "head"-like request is being made (on a file) */ - newport = (unsigned short)(((ip[4] << 8) + ip[5]) & 0xffff); - } - else if(ftpc->count1 == 0) { - /* EPSV failed, move on to PASV */ - return ftp_epsv_disable(data, ftpc, conn); - } - else { - failf(data, "Bad PASV/EPSV response: %03d", ftpcode); - return CURLE_FTP_WEIRD_PASV_REPLY; + /* we know ftpc->file is a valid pointer to a filename */ + result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_SIZE); } + else + result = ftp_state_rest(data, ftpc, ftp); -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy) { - /* This connection uses a proxy and we need to connect to the proxy again - * here. We do not want to rely on a former host lookup that might have - * expired now, instead we remake the lookup here and now! */ - struct ip_quadruple ipquad; - bool is_ipv6; - const char * const host_name = conn->bits.socksproxy ? - conn->socks_proxy.host.name : conn->http_proxy.host.name; + return result; +} - result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, - &is_ipv6, &ipquad); - if(result) - goto error; +static CURLcode ftp_state_list(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + CURLcode result = CURLE_OK; - (void)Curl_resolv_blocking(data, host_name, ipquad.remote_port, - is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4, - &dns); - /* we connect to the proxy's port */ - connectport = (unsigned short)ipquad.remote_port; + /* If this output is to be machine-parsed, the NLST command might be better + to use, since the LIST command output is not specified or standard in any + way. It has turned out that the NLST list output is not the same on all + servers either... */ - if(!dns) { - failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; - } - } - else -#endif - { - /* normal, direct, ftp connection */ - DEBUGASSERT(newhost); + /* + if FTPFILE_NOCWD was specified, we should add the path + as argument for the LIST / NLST / or custom command. + Whether the server will support this, is uncertain. - /* postponed address resolution in case of tcp fastopen */ - if(conn->bits.tcp_fastopen && !conn->bits.reuse && !newhost[0]) { - curlx_free(newhost); - result = ftp_control_addr_dup(data, &newhost); - if(result) - goto error; - } + The other ftp_filemethods will CWD into dir/dir/ first and + then do LIST (in that case: nothing to do here) + */ + const char *lstArg = NULL; + int lstArglen = 0; + char *cmd; - (void)Curl_resolv_blocking(data, newhost, newport, conn->ip_version, &dns); - connectport = newport; /* we connect to the remote port */ + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { + /* url-decode before evaluation: e.g. paths starting/ending with %2f */ + const char *rawPath = ftpc->rawpath; + const char *slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* chop off the file part if format is dir/file otherwise remove + the trailing slash for dir/dir/ except for absolute path / */ + size_t n = slashPos - rawPath; + if(n == 0) + ++n; - if(!dns) { - failf(data, "cannot resolve new host %s:%hu", newhost, connectport); - result = CURLE_FTP_CANT_GET_HOST; - goto error; + lstArg = rawPath; + lstArglen = (int)n; } } - result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, - conn->bits.ftp_use_data_ssl ? - CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); + cmd = curl_maprintf("%s%s%.*s", + data->set.str[STRING_CUSTOMREQUEST] ? + data->set.str[STRING_CUSTOMREQUEST] : + (data->state.list_only ? "NLST" : "LIST"), + lstArg ? " " : "", + lstArglen, lstArg ? lstArg : ""); - if(result) { - if((result != CURLE_OUT_OF_MEMORY) && - (ftpc->count1 == 0) && (ftpcode == 229)) { - curlx_free(newhost); - return ftp_epsv_disable(data, ftpc, conn); - } + if(!cmd) + return CURLE_OUT_OF_MEMORY; - goto error; - } + result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); + curlx_free(cmd); - /* - * When this is used from the multi interface, this might have returned with - * the 'connected' set to FALSE and thus we are now awaiting a non-blocking - * connect to connect. - */ + if(!result) + ftp_state(data, ftpc, FTP_LIST); - if(data->set.verbose) - /* this just dumps information about this second connection */ - ftp_pasv_verbose(data, dns->addr, newhost, connectport); + return result; +} - curlx_free(conn->secondaryhostname); - conn->secondary_port = newport; - conn->secondaryhostname = curlx_strdup(newhost); - if(!conn->secondaryhostname) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } +static CURLcode ftp_state_list_prequote(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + /* We have sent the TYPE, now we must send the list of prequote strings */ + return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_LIST_PREQUOTE); +} - conn->bits.do_more = TRUE; - ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */ +static CURLcode ftp_state_retr_prequote(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + /* We have sent the TYPE, now we must send the list of prequote strings */ + return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE); +} -error: - curlx_free(newhost); - return result; +static CURLcode ftp_state_stor_prequote(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + /* We have sent the TYPE, now we must send the list of prequote strings */ + return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_STOR_PREQUOTE); } -static CURLcode ftp_state_port_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - int ftpcode) +static CURLcode ftp_state_type(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) { - struct connectdata *conn = data->conn; - ftpport fcmd = (ftpport)ftpc->count1; CURLcode result = CURLE_OK; - /* The FTP spec tells a positive response should have code 200. - Be more permissive here to tolerate deviant servers. */ - if(ftpcode / 100 != 2) { - /* the command failed */ + /* If we have selected NOBODY and HEADER, it means that we only want file + information. Which in FTP cannot be much more than the file size and + date. */ + if(data->req.no_body && ftpc->file && + ftp_need_type(ftpc, (bool)data->state.prefer_ascii)) { + /* The SIZE command is _not_ RFC 959 specified, and therefore many servers + may not support it! It is however the only way we have to get a file's + size! */ - if(EPRT == fcmd) { - infof(data, "disabling EPRT usage"); - conn->bits.ftp_use_eprt = FALSE; - } - fcmd++; + ftp->transfer = PPTRANSFER_INFO; + /* this means no actual transfer will be made */ - if(fcmd == DONE) { - failf(data, "Failed to do PORT"); - result = CURLE_FTP_PORT_FAILED; - } - else - /* try next */ - result = ftp_state_use_port(data, ftpc, fcmd); - } - else { - infof(data, "Connect data stream actively"); - ftp_state(data, ftpc, FTP_STOP); /* end of DO phase */ - result = ftp_dophase_done(data, ftpc, ftp, FALSE); + /* Some servers return different sizes for different modes, and thus we + must set the proper type before we check the size */ + result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii, + FTP_TYPE); + if(result) + return result; } + else + result = ftp_state_size(data, ftpc, ftp); return result; } -static int twodigit(const char *p) +/* This is called after the CWD commands have been done in the beginning of + the DO phase */ +static CURLcode ftp_state_mdtm(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) { - return (p[0] - '0') * 10 + (p[1] - '0'); -} + CURLcode result = CURLE_OK; -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]); + /* Requested time of file or time-depended transfer? */ + if((data->set.get_filetime || data->set.timecondition) && ftpc->file) { - if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) || - (*second > 60)) - return FALSE; - return TRUE; -} + /* we have requested to get the modified-time of the file, this is a white + spot as the MDTM is not mentioned in RFC959 */ + result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file); + + if(!result) + ftp_state(data, ftpc, FTP_MDTM); + } + else + result = ftp_state_type(data, ftpc, ftp); -static CURLcode client_write_header(struct Curl_easy *data, - char *buf, size_t blen) -{ - /* Some replies from an FTP server are written to the client - * as CLIENTWRITE_HEADER, formatted as if they came from a - * HTTP conversation. - * In all protocols, CLIENTWRITE_HEADER data is only passed to - * the body write callback when data->set.include_header is set - * via CURLOPT_HEADER. - * For historic reasons, FTP never played this game and expects - * all its headers to do that always. Set that flag during the - * call to Curl_client_write() so it does the right thing. - * - * Notice that we cannot enable this flag for FTP in general, - * as an FTP transfer might involve an HTTP proxy connection and - * headers from CONNECT should not automatically be part of the - * output. */ - CURLcode result; - bool save = data->set.include_header; - data->set.include_header = TRUE; - result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen); - data->set.include_header = save; return result; } -static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - int ftpcode) +/* This is called after the TYPE and possible quote commands have been sent */ +static CURLcode ftp_state_ul_setup(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool sizechecked) { CURLcode result = CURLE_OK; + curl_bit append = data->set.remote_append; - switch(ftpcode) { - case 213: { - /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the - last .sss part is optional and means fractions of a second */ - int year, month, day, hour, minute, second; - struct pingpong *pp = &ftpc->pp; - char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4; - bool showtime = FALSE; - if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) { - /* we have a time, reformat it */ - char timebuf[24]; - curl_msnprintf(timebuf, sizeof(timebuf), - "%04d%02d%02d %02d:%02d:%02d GMT", - year, month, day, hour, minute, second); - /* now, convert this into a time() value: */ - if(!Curl_getdate_capped(timebuf, &data->info.filetime)) - showtime = TRUE; + if((data->state.resume_from && !sizechecked) || + ((data->state.resume_from > 0) && sizechecked)) { + /* we are about to continue the uploading of a file */ + /* 1. get already existing file's size. We use the SIZE command for this + which may not exist in the server! The SIZE command is not in + RFC959. */ + + /* 2. This used to set REST. But since we can do append, we issue no + another ftp command. Skip the source file offset and APPEND the rest on + the file instead */ + + /* 3. pass file-size number of bytes in the source file */ + /* 4. lower the infilesize counter */ + /* => transfer as usual */ + int seekerr = CURL_SEEKFUNC_OK; + + if(data->state.resume_from < 0) { + /* Got no given size to start from, figure it out */ + result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_STOR_SIZE); + return result; } - /* If we asked for a time of the file and we actually got one as well, - we "emulate" an HTTP-style header in our output. */ + /* enable append */ + append = TRUE; -#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) -#pragma GCC diagnostic push -/* 'time_t' is unsigned in MSDOS and AmigaOS. Silence: - warning: comparison of unsigned expression in '>= 0' is always true */ -#pragma GCC diagnostic ignored "-Wtype-limits" -#endif - if(data->req.no_body && ftpc->file && - data->set.get_filetime && showtime) { -#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) -#pragma GCC diagnostic pop -#endif - char headerbuf[128]; - int headerbuflen; - time_t filetime = data->info.filetime; - struct tm buffer; - const struct tm *tm = &buffer; + /* Let's read off the proper amount of bytes from the input. */ + if(data->set.seek_func) { + Curl_set_in_callback(data, TRUE); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); + Curl_set_in_callback(data, FALSE); + } - result = curlx_gmtime(filetime, &buffer); - if(result) - return result; + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ + do { + char scratch[4 * 1024]; + size_t readthisamountnow = + (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : + curlx_sotouz(data->state.resume_from - passed); - /* format: "Tue, 15 Nov 1994 12:45:26" */ - headerbuflen = - curl_msnprintf(headerbuf, sizeof(headerbuf), - "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d " - "GMT\r\n", - Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6], - tm->tm_mday, - Curl_month[tm->tm_mon], - tm->tm_year + 1900, - tm->tm_hour, - tm->tm_min, - tm->tm_sec); - result = client_write_header(data, headerbuf, headerbuflen); - if(result) - return result; - } /* end of a ridiculous amount of conditionals */ - } - break; - default: - infof(data, "unsupported MDTM reply format"); - break; - case 550: /* 550 is used for several different problems, e.g. - "No such file or directory" or "Permission denied". - It does not mean that the file does not exist at all. */ - infof(data, "MDTM failed: file does not exist or permission problem," - " continuing"); - break; - } + size_t actuallyread = + data->state.fread_func(scratch, 1, readthisamountnow, + data->state.in); - if(data->set.timecondition) { - if((data->info.filetime > 0) && (data->set.timevalue > 0)) { - switch(data->set.timecondition) { - case CURL_TIMECOND_IFMODSINCE: - default: - if(data->info.filetime <= data->set.timevalue) { - infof(data, "The requested document is not new enough"); - ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ - data->info.timecond = TRUE; - ftp_state(data, ftpc, FTP_STOP); - return CURLE_OK; - } - break; - case CURL_TIMECOND_IFUNMODSINCE: - if(data->info.filetime > data->set.timevalue) { - infof(data, "The requested document is not old enough"); - ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ - data->info.timecond = TRUE; - ftp_state(data, ftpc, FTP_STOP); - return CURLE_OK; + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; } - break; - } /* switch */ - } - else { - infof(data, "Skipping time comparison"); + } while(passed < data->state.resume_from); } - } + /* now, decrease the size of the read */ + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; - if(!result) - result = ftp_state_type(data, ftpc, ftp); + if(data->state.infilesize <= 0) { + infof(data, "File already completely uploaded"); - return result; -} + /* no data to transfer */ + Curl_xfer_setup_nop(data); -static CURLcode ftp_state_type_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - int ftpcode, - ftpstate instate) -{ - CURLcode result = CURLE_OK; + /* Set ->transfer so that we will not get any error in + * ftp_done() because we did not transfer anything! */ + ftp->transfer = PPTRANSFER_NONE; - if(ftpcode / 100 != 2) { - /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a - successful 'TYPE I'. While that is not as RFC959 says, it is still a - positive response code and we allow that. */ - failf(data, "Could not set desired mode"); - return CURLE_FTP_COULDNT_SET_TYPE; - } - if(ftpcode != 200) - infof(data, "Got a %03d response code instead of the assumed 200", - ftpcode); + ftp_state(data, ftpc, FTP_STOP); + return CURLE_OK; + } + } + /* we have passed, proceed as normal */ + } /* resume_from */ - if(instate == FTP_TYPE) - result = ftp_state_size(data, ftpc, ftp); - else if(instate == FTP_LIST_TYPE) - result = ftp_state_list(data, ftpc, ftp); - else if(instate == FTP_RETR_TYPE) - result = ftp_state_retr_prequote(data, ftpc, ftp); - else if(instate == FTP_STOR_TYPE) - result = ftp_state_stor_prequote(data, ftpc, ftp); - else if(instate == FTP_RETR_LIST_TYPE) - result = ftp_state_list_prequote(data, ftpc, ftp); + result = Curl_pp_sendf(data, &ftpc->pp, append ? "APPE %s" : "STOR %s", + ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_STOR); return result; } @@ -2230,14 +1791,14 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, this even when not doing resumes. */ if(filesize == -1) { infof(data, "ftp server does not support SIZE"); - /* We could not get the size and therefore we cannot know if there really - is a part of the file left to get, although the server will just - close the connection when we start the connection so it will not cause - us any harm, just not make us exit as nicely. */ + /* We could not get the size and therefore we cannot know if there + really is a part of the file left to get, although the server will + close the connection when we start the connection so it will not + cause us any harm, not make us exit as nicely. */ } else { /* We got a file size report, so we check that there actually is a - part of the file left to get, or else we go home. */ + part of the file left to get, or else we go home. */ if(data->state.resume_from < 0) { /* We are supposed to download the last abs(from) bytes */ if(filesize < -data->state.resume_from) { @@ -2294,2010 +1855,2291 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, return result; } -static CURLcode ftp_state_size_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - int ftpcode, - ftpstate instate) +static CURLcode ftp_state_quote(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool init, + ftpstate instate) { CURLcode result = CURLE_OK; - curl_off_t filesize = -1; - char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); - size_t len = ftpc->pp.nfinal; + bool quote = FALSE; + struct curl_slist *item; - /* get the size from the ascii string: */ - if(ftpcode == 213) { - /* To allow servers to prepend "rubbish" in the response string, we scan - for all the digits at the end of the response and parse only those as a - number. */ - char *start = &buf[4]; - const char *fdigit = memchr(start, '\r', len - 4); - if(fdigit) { - fdigit--; - if(*fdigit == '\n') - fdigit--; - while(ISDIGIT(fdigit[-1]) && (fdigit > start)) - fdigit--; - } - else - fdigit = start; - if(curlx_str_number(&fdigit, &filesize, CURL_OFF_T_MAX)) - filesize = -1; /* size remain unknown */ + switch(instate) { + case FTP_QUOTE: + default: + item = data->set.quote; + break; + case FTP_RETR_PREQUOTE: + case FTP_STOR_PREQUOTE: + case FTP_LIST_PREQUOTE: + item = data->set.prequote; + break; + case FTP_POSTQUOTE: + item = data->set.postquote; + break; } - else if(ftpcode == 550) { /* "No such file or directory" */ - /* allow a SIZE failure for (resumed) uploads, when probing what command - to use */ - if(instate != FTP_STOR_SIZE) { - failf(data, "The file does not exist"); - return CURLE_REMOTE_FILE_NOT_FOUND; + + /* + * This state uses: + * 'count1' to iterate over the commands to send + * 'count2' to store whether to allow commands to fail + */ + + if(init) + ftpc->count1 = 0; + else + ftpc->count1++; + + if(item) { + int i = 0; + + /* Skip count1 items in the linked list */ + while((i < ftpc->count1) && item) { + item = item->next; + i++; } - } + if(item) { + const char *cmd = item->data; + if(cmd[0] == '*') { + cmd++; + ftpc->count2 = 1; /* the sent command is allowed to fail */ + } + else + ftpc->count2 = 0; /* failure means cancel operation */ - if(instate == FTP_SIZE) { - if(filesize != -1) { - char clbuf[128]; - int clbuflen = curl_msnprintf(clbuf, sizeof(clbuf), - "Content-Length: %" FMT_OFF_T "\r\n", - filesize); - result = client_write_header(data, clbuf, clbuflen); + result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); if(result) return result; + ftp_state(data, ftpc, instate); + quote = TRUE; } - Curl_pgrsSetDownloadSize(data, filesize); - result = ftp_state_rest(data, ftpc, ftp); - } - else if(instate == FTP_RETR_SIZE) { - Curl_pgrsSetDownloadSize(data, filesize); - result = ftp_state_retr(data, ftpc, ftp, filesize); } - else if(instate == FTP_STOR_SIZE) { - data->state.resume_from = filesize; - result = ftp_state_ul_setup(data, ftpc, ftp, TRUE); + + if(!quote) { + /* No more quote to send, continue to ... */ + switch(instate) { + case FTP_QUOTE: + default: + result = ftp_state_cwd(data, ftpc, ftp); + break; + case FTP_RETR_PREQUOTE: + if(ftp->transfer != PPTRANSFER_BODY) + ftp_state(data, ftpc, FTP_STOP); + else { + if(ftpc->known_filesize != -1) { + Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); + result = ftp_state_retr(data, ftpc, ftp, ftpc->known_filesize); + } + else { + if(data->set.ignorecl || data->state.prefer_ascii) { + /* 'ignorecl' is used to support download of growing files. It + prevents the state machine from requesting the file size from + the server. With an unknown file size the download continues + until the server terminates it, otherwise the client stops if + the received byte count exceeds the reported file size. Set + option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this + behavior. + + In addition: asking for the size for 'TYPE A' transfers is not + constructive since servers do not report the converted size. So + skip it. + */ + result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_RETR); + } + else { + result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_RETR_SIZE); + } + } + } + break; + case FTP_STOR_PREQUOTE: + result = ftp_state_ul_setup(data, ftpc, ftp, FALSE); + break; + case FTP_POSTQUOTE: + break; + case FTP_LIST_PREQUOTE: + ftp_state(data, ftpc, FTP_LIST_TYPE); + result = ftp_state_list(data, ftpc, ftp); + break; + } } return result; } -static CURLcode ftp_state_rest_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - int ftpcode, - ftpstate instate) +/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV + problems */ +static CURLcode ftp_epsv_disable(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct connectdata *conn) { CURLcode result = CURLE_OK; - switch(instate) { - case FTP_REST: - default: - if(ftpcode == 350) { - char buffer[24] = { "Accept-ranges: bytes\r\n" }; - result = client_write_header(data, buffer, strlen(buffer)); - if(result) - return result; - } - result = ftp_state_prepare_transfer(data, ftpc, ftp); - break; - - case FTP_RETR_REST: - if(ftpcode != 350) { - failf(data, "Could not use REST"); - result = CURLE_FTP_COULDNT_USE_REST; - } - else { - result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_RETR); - } - break; + if(conn->bits.ipv6 +#ifndef CURL_DISABLE_PROXY + && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) +#endif + ) { + /* We cannot disable EPSV when doing IPv6, so this is instead a fail */ + failf(data, "Failed EPSV attempt, exiting"); + return CURLE_WEIRD_SERVER_REPLY; } + infof(data, "Failed EPSV attempt. Disabling EPSV"); + /* disable it for next transfer */ + conn->bits.ftp_use_epsv = FALSE; + close_secondarysocket(data, ftpc); + data->state.errorbuf = FALSE; /* allow error message to get + rewritten */ + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PASV"); + if(!result) { + ftpc->count1++; + /* remain in/go to the FTP_PASV state */ + ftp_state(data, ftpc, FTP_PASV); + } return result; } -static CURLcode ftp_state_stor_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - int ftpcode) +static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp) { - CURLcode result = CURLE_OK; - - if(ftpcode >= 400) { - failf(data, "Failed FTP upload: %0d", ftpcode); - ftp_state(data, ftpc, FTP_STOP); - return CURLE_UPLOAD_FAILED; - } - - /* PORT means we are now awaiting the server to connect to us. */ - if(data->set.ftp_use_port) { - bool connected; + struct connectdata *conn = data->conn; + struct ip_quadruple ipquad; + bool is_ipv6; - ftp_state(data, ftpc, FTP_STOP); /* no longer in STOR state */ - - result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); - if(result) - return result; + /* Returns the control connection IP address. + If a proxy tunnel is used, returns the original hostname instead, because + the effective control connection address is the proxy address, + not the ftp host. */ +#ifndef CURL_DISABLE_PROXY + if(conn->bits.tunnel_proxy || conn->bits.socksproxy) + *newhostp = curlx_strdup(conn->host.name); + else +#endif + if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) && + *ipquad.remote_ip) + *newhostp = curlx_strdup(ipquad.remote_ip); + else { + /* failed to get the remote_ip of the DATA connection */ + failf(data, "unable to get peername of DATA connection"); + *newhostp = NULL; + return CURLE_FTP_CANT_GET_HOST; + } + return *newhostp ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} - if(!connected) { - infof(data, "Data conn was not available immediately"); - ftpc->wait_data_conn = TRUE; - return ftp_check_ctrl_on_data_wait(data, ftpc); +static bool match_pasv_6nums(const char *p, + unsigned int *array) /* 6 numbers */ +{ + int i; + for(i = 0; i < 6; i++) { + curl_off_t num; + if(i) { + if(*p != ',') + return FALSE; + p++; } - ftpc->wait_data_conn = FALSE; + if(curlx_str_number(&p, &num, 0xff)) + return FALSE; + array[i] = (unsigned int)num; } - return ftp_initiate_transfer(data, ftpc); + return TRUE; } -/* for LIST and RETR responses */ -static CURLcode ftp_state_get_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - int ftpcode, - ftpstate instate) +static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + int ftpcode) { - CURLcode result = CURLE_OK; - - if((ftpcode == 150) || (ftpcode == 125)) { - - /* - A; - 150 Opening BINARY mode data connection for /etc/passwd (2241 - bytes). (ok, the file is being transferred) - - B: - 150 Opening ASCII mode data connection for /bin/ls - - C: - 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes). - - D: - 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes) - - E: - 125 Data connection already open; Transfer starting. */ - - data->req.size = -1; /* default unknown size */ + struct connectdata *conn = data->conn; + CURLcode result; + struct Curl_dns_entry *dns = NULL; + unsigned short connectport; /* the local port connect() should use! */ + const struct pingpong *pp = &ftpc->pp; + char *newhost = NULL; + unsigned short newport = 0; + const char *str = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ + if((ftpc->count1 == 0) && + (ftpcode == 229)) { + /* positive EPSV response */ + const char *ptr = strchr(str, '('); + if(ptr) { + char sep; + ptr++; + /* |||12345| */ + sep = ptr[0]; + if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { + const char *p = &ptr[3]; + curl_off_t num; + if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) { + failf(data, "Illegal port number in EPSV reply"); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + newport = (unsigned short)num; + result = ftp_control_addr_dup(data, &newhost); + if(result) + return result; + } + else + ptr = NULL; + } + if(!ptr) { + failf(data, "Weirdly formatted EPSV reply"); + return CURLE_FTP_WEIRD_PASV_REPLY; + } + } + else if((ftpc->count1 == 1) && + (ftpcode == 227)) { + /* positive PASV response */ + unsigned int ip[6]; /* - * It appears that there are FTP-servers that return size 0 for files when - * SIZE is used on the file while being in BINARY mode. To work around - * that (stupid) behavior, we attempt to parse the RETR response even if - * the SIZE returned size zero. + * Scan for a sequence of six comma-separated numbers and use them as + * IP+port indicators. * - * Debugging help from Salvatore Sorrentino on February 26, 2003. + * Found reply-strings include: + * "227 Entering Passive Mode (127,0,0,1,4,51)" + * "227 Data transfer will passively listen to 127,0,0,1,4,51" + * "227 Entering passive mode. 127,0,0,1,4,51" */ - - if((instate != FTP_LIST) && - !data->state.prefer_ascii && - !data->set.ignorecl && - (ftp->downloadsize < 1)) { - /* - * It seems directory listings either do not show the size or often uses - * size 0 anyway. ASCII transfers may cause that the transferred amount - * of data is not the same as this line tells, why using this number in - * those cases only confuses us. - * - * Example D above makes this parsing a little tricky */ - size_t len = curlx_dyn_len(&ftpc->pp.recvbuf); - if(len >= 7) { /* "1 bytes" is 7 characters */ - size_t i; - for(i = 0; i < len - 7; i++) { - curl_off_t what; - char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); - const char *c = &buf[i]; - if(!curlx_str_number(&c, &what, CURL_OFF_T_MAX) && - !curlx_str_single(&c, ' ') && - !strncmp(c, "bytes", 5)) { - data->req.size = what; - break; - } - } - } + while(*str) { + if(match_pasv_6nums(str, ip)) + break; + str++; } - else if(ftp->downloadsize > -1) - data->req.size = ftp->downloadsize; - - if(data->req.size > data->req.maxdownload && data->req.maxdownload > 0) - data->req.size = data->req.maxdownload; - else if((instate != FTP_LIST) && (data->state.prefer_ascii)) - data->req.size = -1; /* for servers that understate ASCII mode file - size */ - - infof(data, "Maxdownload = %" FMT_OFF_T, data->req.maxdownload); - if(instate != FTP_LIST) - infof(data, "Getting file with size: %" FMT_OFF_T, data->req.size); - - if(data->set.ftp_use_port) { - bool connected; + if(!*str) { + failf(data, "Could not interpret the 227-response"); + return CURLE_FTP_WEIRD_227_FORMAT; + } - result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); + /* we got OK from server */ + if(data->set.ftp_skip_ip) { + /* told to ignore the remotely given IP but instead use the host we used + for the control connection */ + infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead", + ip[0], ip[1], ip[2], ip[3], conn->host.name); + result = ftp_control_addr_dup(data, &newhost); if(result) return result; - - if(!connected) { - infof(data, "Data conn was not available immediately"); - ftp_state(data, ftpc, FTP_STOP); - ftpc->wait_data_conn = TRUE; - return ftp_check_ctrl_on_data_wait(data, ftpc); - } - ftpc->wait_data_conn = FALSE; } - return ftp_initiate_transfer(data, ftpc); + else + newhost = curl_maprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + + if(!newhost) + return CURLE_OUT_OF_MEMORY; + + newport = (unsigned short)(((ip[4] << 8) + ip[5]) & 0xffff); + } + else if(ftpc->count1 == 0) { + /* EPSV failed, move on to PASV */ + return ftp_epsv_disable(data, ftpc, conn); } else { - if((instate == FTP_LIST) && (ftpcode == 450)) { - /* simply no matching files in the directory listing */ - ftp->transfer = PPTRANSFER_NONE; /* do not download anything */ - ftp_state(data, ftpc, FTP_STOP); /* this phase is over */ - } - else { - failf(data, "RETR response: %03d", ftpcode); - return instate == FTP_RETR && ftpcode == 550 ? - CURLE_REMOTE_FILE_NOT_FOUND : - CURLE_FTP_COULDNT_RETR_FILE; - } + failf(data, "Bad PASV/EPSV response: %03d", ftpcode); + return CURLE_FTP_WEIRD_PASV_REPLY; } - return result; -} - -/* after USER, PASS and ACCT */ -static CURLcode ftp_state_loggedin(struct Curl_easy *data, - struct ftp_conn *ftpc) -{ - CURLcode result = CURLE_OK; +#ifndef CURL_DISABLE_PROXY + if(conn->bits.proxy) { + /* This connection uses a proxy and we need to connect to the proxy again + * here. We do not want to rely on a former host lookup that might have + * expired now, instead we remake the lookup here and now! */ + struct ip_quadruple ipquad; + bool is_ipv6; + const char * const host_name = conn->bits.socksproxy ? + conn->socks_proxy.host.name : conn->http_proxy.host.name; - if(data->conn->bits.ftp_use_control_ssl) { - /* PBSZ = PROTECTION BUFFER SIZE. + result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, + &is_ipv6, &ipquad); + if(result) + goto error; - The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: + (void)Curl_resolv_blocking(data, host_name, ipquad.remote_port, + is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4, + &dns); + /* we connect to the proxy's port */ + connectport = (unsigned short)ipquad.remote_port; - Specifically, the PROT command MUST be preceded by a PBSZ - command and a PBSZ command MUST be preceded by a successful - security data exchange (the TLS negotiation in this case) + if(!dns) { + failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } + } + else +#endif + { + /* normal, direct, ftp connection */ + DEBUGASSERT(newhost); - ... (and on page 8): + /* postponed address resolution in case of tcp fastopen */ + if(conn->bits.tcp_fastopen && !conn->bits.reuse && !newhost[0]) { + curlx_free(newhost); + result = ftp_control_addr_dup(data, &newhost); + if(result) + goto error; + } - Thus the PBSZ command must still be issued, but must have a - parameter of '0' to indicate that no buffering is taking place - and the data connection should not be encapsulated. - */ - result = Curl_pp_sendf(data, &ftpc->pp, "PBSZ %d", 0); - if(!result) - ftp_state(data, ftpc, FTP_PBSZ); - } - else { - result = ftp_state_pwd(data, ftpc); + (void)Curl_resolv_blocking(data, newhost, newport, conn->ip_version, &dns); + connectport = newport; /* we connect to the remote port */ + + if(!dns) { + failf(data, "cannot resolve new host %s:%hu", newhost, connectport); + result = CURLE_FTP_CANT_GET_HOST; + goto error; + } } - return result; -} -/* for USER and PASS responses */ -static CURLcode ftp_state_user_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - int ftpcode) -{ - CURLcode result = CURLE_OK; + result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, + conn->bits.ftp_use_data_ssl ? + CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); - /* some need password anyway, and others just return 2xx ignored */ - if((ftpcode == 331) && (ftpc->state == FTP_USER)) { - /* 331 Password required for ... - (the server requires to send the user's password too) */ - result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", data->conn->passwd); - if(!result) - ftp_state(data, ftpc, FTP_PASS); - } - else if(ftpcode / 100 == 2) { - /* 230 User ... logged in. - (the user logged in with or without password) */ - result = ftp_state_loggedin(data, ftpc); - } - else if(ftpcode == 332) { - if(data->set.str[STRING_FTP_ACCOUNT]) { - result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", - data->set.str[STRING_FTP_ACCOUNT]); - if(!result) - ftp_state(data, ftpc, FTP_ACCT); - } - else { - failf(data, "ACCT requested but none available"); - result = CURLE_LOGIN_DENIED; + if(result) { + if((result != CURLE_OUT_OF_MEMORY) && + (ftpc->count1 == 0) && (ftpcode == 229)) { + curlx_free(newhost); + return ftp_epsv_disable(data, ftpc, conn); } + + goto error; } - else { - /* All other response codes, like: - 530 User ... access denied - (the server denies to log the specified user) */ + /* + * When this is used from the multi interface, this might have returned with + * the 'connected' set to FALSE and thus we are now awaiting a non-blocking + * connect to connect. + */ - if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] && - !ftpc->ftp_trying_alternative) { - /* Ok, USER failed. Let's try the supplied command. */ - result = Curl_pp_sendf(data, &ftpc->pp, "%s", - data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); - if(!result) { - ftpc->ftp_trying_alternative = TRUE; - ftp_state(data, ftpc, FTP_USER); - } - } - else { - failf(data, "Access denied: %03d", ftpcode); - result = CURLE_LOGIN_DENIED; - } +#ifdef CURLVERBOSE + if(data->set.verbose) { + /* Dump information about this second connection when we have issued a PASV + * command before and thus we have connected to a possibly new IP address. + */ + char buf[256]; + Curl_printable_address(dns->addr, buf, sizeof(buf)); + infof(data, "Connecting to %s (%s) port %d", newhost, buf, connectport); } - return result; -} +#endif -/* for ACCT response */ -static CURLcode ftp_state_acct_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - int ftpcode) -{ - CURLcode result = CURLE_OK; - if(ftpcode != 230) { - failf(data, "ACCT rejected by server: %03d", ftpcode); - result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */ + curlx_free(conn->secondaryhostname); + conn->secondary_port = newport; + conn->secondaryhostname = curlx_strdup(newhost); + if(!conn->secondaryhostname) { + result = CURLE_OUT_OF_MEMORY; + goto error; } - else - result = ftp_state_loggedin(data, ftpc); + conn->bits.do_more = TRUE; + ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */ + +error: + curlx_free(newhost); return result; } -static CURLcode ftp_pwd_resp(struct Curl_easy *data, - struct ftp_conn *ftpc, - int ftpcode) +/* called repeatedly until done from multi.c */ +static CURLcode ftp_statemach(struct Curl_easy *data, + struct ftp_conn *ftpc, + bool *done) { - struct pingpong *pp = &ftpc->pp; - CURLcode result; - - if(ftpcode == 257) { - char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first - letter */ - bool entry_extracted = FALSE; - struct dynbuf out; - curlx_dyn_init(&out, 1000); - - /* Reply format is like - 257[rubbish]"" and the - RFC959 says + CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); - The directory name can contain any character; embedded - double-quotes should be escaped by double-quotes (the - "quote-doubling" convention). - */ + /* Check for the state outside of the Curl_socket_check() return code checks + since at times we are in fact already in this state when this function + gets called. */ + *done = (ftpc->state == FTP_STOP); - /* scan for the first double-quote for non-standard responses */ - while(*ptr != '\n' && *ptr != '\0' && *ptr != '"') - ptr++; + return result; +} - if('\"' == *ptr) { - /* it started good */ - for(ptr++; *ptr; ptr++) { - if('\"' == *ptr) { - if('\"' == ptr[1]) { - /* "quote-doubling" */ - result = curlx_dyn_addn(&out, &ptr[1], 1); - ptr++; - } - else { - /* end of path */ - if(curlx_dyn_len(&out)) - entry_extracted = TRUE; - break; /* get out of this loop */ - } - } - else - result = curlx_dyn_addn(&out, ptr, 1); - if(result) - return result; - } - } - if(entry_extracted) { - /* If the path name does not look like an absolute path (i.e.: it - does not start with a '/'), we probably need some server-dependent - adjustments. For example, this is the case when connecting to - an OS400 FTP server: this server supports two name syntaxes, - the default one being incompatible with standard paths. In - addition, this server switches automatically to the regular path - syntax when one is encountered in a command: this results in - having an entrypath in the wrong syntax when later used in CWD. - The method used here is to check the server OS: we do it only - if the path name looks strange to minimize overhead on other - systems. */ - char *dir = curlx_dyn_ptr(&out); +/* + * ftp_do_more() + * + * This function shall be called when the second FTP (data) connection is + * connected. + * + * 'complete' can return 0 for incomplete, 1 for done and -1 for go back + * (which is for when PASV is being sent to retry a failed EPSV). + */ +static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) +{ + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); + CURLcode result = CURLE_OK; + bool connected = FALSE; + bool complete = FALSE; + /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP + * proxy then the state will not be valid until after that connection is + * complete */ - if(!ftpc->server_os && dir[0] != '/') { - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST"); - if(result) { - curlx_free(dir); - return result; - } - } + if(!ftpc || !ftp) + return CURLE_FAILED_INIT; - curlx_free(ftpc->entrypath); - ftpc->entrypath = dir; /* remember this */ - infof(data, "Entry path is '%s'", ftpc->entrypath); - /* also save it where getinfo can access it: */ - curlx_free(data->state.most_recent_ftp_entrypath); - data->state.most_recent_ftp_entrypath = curlx_strdup(ftpc->entrypath); - if(!data->state.most_recent_ftp_entrypath) - return CURLE_OUT_OF_MEMORY; + *completep = 0; /* default to stay in the state */ - if(!ftpc->server_os && dir[0] != '/') { - ftp_state(data, ftpc, FTP_SYST); - return CURLE_OK; + /* if the second connection has been set up, try to connect it fully + * to the remote host. This may not complete at this time, for several + * reasons: + * - we do EPTR and the server will not connect to our listen socket + * until we send more FTP commands + * - an SSL filter is in place and the server will not start the TLS + * handshake until we send more FTP commands + */ + if(conn->cfilter[SECONDARYSOCKET]) { + bool is_eptr = Curl_conn_is_tcp_listen(data, SECONDARYSOCKET); + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); + if(result == CURLE_OUT_OF_MEMORY) + return result; + if(result || (!connected && !is_eptr && + !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) { + if(result && !is_eptr && (ftpc->count1 == 0)) { + *completep = -1; /* go back to DOING please */ + /* this is a EPSV connect failing, try PASV instead */ + return ftp_epsv_disable(data, ftpc, conn); } - } - else { - /* could not get the path */ - curlx_dyn_free(&out); - infof(data, "Failed to figure out path"); + return result; } } - ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */ - CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc)); - return CURLE_OK; -} -static const char * const ftpauth[] = { "SSL", "TLS" }; + if(ftpc->state) { + /* already in a state so skip the initial commands. + They are only done to kickstart the do_more state */ + result = ftp_statemach(data, ftpc, &complete); -static CURLcode ftp_wait_resp(struct Curl_easy *data, - struct connectdata *conn, - struct ftp_conn *ftpc, - int ftpcode) -{ - CURLcode result = CURLE_OK; - if(ftpcode == 230) { - /* 230 User logged in - already! Take as 220 if TLS required. */ - if(data->set.use_ssl <= CURLUSESSL_TRY || - conn->bits.ftp_use_control_ssl) - return ftp_state_user_resp(data, ftpc, ftpcode); - } - else if(ftpcode != 220) { - failf(data, "Got a %03d ftp-server response when 220 was expected", - ftpcode); - return CURLE_WEIRD_SERVER_REPLY; - } + *completep = (int)complete; - if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) { - /* We do not have an SSL/TLS control connection yet, but FTPS is - requested. Try an FTPS connection now */ + /* if we got an error or if we do not wait for a data connection return + immediately */ + if(result || !ftpc->wait_data_conn) + return result; - ftpc->count3 = 0; - switch((long)data->set.ftpsslauth) { - case CURLFTPAUTH_DEFAULT: - case CURLFTPAUTH_SSL: - ftpc->count2 = 1; /* add one to get next */ - ftpc->count1 = 0; - break; - case CURLFTPAUTH_TLS: - ftpc->count2 = -1; /* subtract one to get next */ - ftpc->count1 = 1; - break; - default: - failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d", - (int)data->set.ftpsslauth); - return CURLE_UNKNOWN_OPTION; /* we do not know what to do */ - } - result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]); - if(!result) - ftp_state(data, ftpc, FTP_AUTH); + /* if we reach the end of the FTP state machine here, *complete will be + TRUE but so is ftpc->wait_data_conn, which says we need to wait for the + data connection and therefore we are not actually complete */ + *completep = 0; } - else - result = ftp_state_user(data, ftpc, conn); - return result; -} - -static CURLcode ftp_pp_statemachine(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - int ftpcode; - struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); - struct pingpong *pp; - size_t nread = 0; - - if(!ftpc || !ftp) - return CURLE_FAILED_INIT; - pp = &ftpc->pp; - if(pp->sendleft) - return Curl_pp_flushsend(data, pp); - result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, &ftpcode, &nread); - if(result || !ftpcode) - return result; + if(ftp->transfer <= PPTRANSFER_INFO) { + /* a transfer is about to take place, or if not a filename was given so we + will do a SIZE on it later and then we need the right TYPE first */ - /* we have now received a full FTP server response */ - switch(ftpc->state) { - case FTP_WAIT220: - result = ftp_wait_resp(data, conn, ftpc, ftpcode); - break; + if(ftpc->wait_data_conn) { + bool serv_conned; - case FTP_AUTH: - /* we have gotten the response to a previous AUTH command */ + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned); + if(result) + return result; /* Failed to accept data connection */ - if(pp->overflow) - return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */ + if(serv_conned) { + /* It looks data connection is established */ + ftpc->wait_data_conn = FALSE; + result = ftp_initiate_transfer(data, ftpc); - /* RFC2228 (page 5) says: - * - * If the server is willing to accept the named security mechanism, - * and does not require any security data, it must respond with - * reply code 234/334. - */ + if(result) + return result; - if((ftpcode == 234) || (ftpcode == 334)) { - /* this was BLOCKING, keep it so for now */ - bool done; - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); - if(result) { - /* we failed and bail out */ - return CURLE_USE_SSL_FAILED; - } + *completep = 1; /* this state is now complete when the server has + connected back to us */ } - result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done); - if(!result) { - conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ - conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ - result = ftp_state_user(data, ftpc, conn); + else { + result = ftp_check_ctrl_on_data_wait(data, ftpc); + if(result) + return result; } } - else if(ftpc->count3 < 1) { - ftpc->count3++; - ftpc->count1 += ftpc->count2; /* get next attempt */ - result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", - ftpauth[ftpc->count1]); - /* remain in this same state */ + else if(data->state.upload) { + result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii, + FTP_STOR_TYPE); + if(result) + return result; + + result = ftp_statemach(data, ftpc, &complete); + /* ftp_nb_type() might have skipped sending `TYPE A|I` when not + * deemed necessary and directly sent `STORE name`. If this was + * then complete, but we are still waiting on the data connection, + * the transfer has not been initiated yet. */ + *completep = (int)(ftpc->wait_data_conn ? 0 : complete); } else { - if(data->set.use_ssl > CURLUSESSL_TRY) - /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */ - result = CURLE_USE_SSL_FAILED; - else - /* ignore the failure and continue */ - result = ftp_state_user(data, ftpc, conn); - } - break; - - case FTP_USER: - case FTP_PASS: - result = ftp_state_user_resp(data, ftpc, ftpcode); - break; + /* download */ + ftp->downloadsize = -1; /* unknown as of yet */ - case FTP_ACCT: - result = ftp_state_acct_resp(data, ftpc, ftpcode); - break; + result = Curl_range(data); - case FTP_PBSZ: - result = - Curl_pp_sendf(data, &ftpc->pp, "PROT %c", - data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); - if(!result) - ftp_state(data, ftpc, FTP_PROT); - break; + if(result == CURLE_OK && data->req.maxdownload >= 0) { + /* Do not check for successful transfer */ + ftpc->dont_check = TRUE; + } - case FTP_PROT: - if(ftpcode / 100 == 2) - /* We have enabled SSL for the data connection! */ - conn->bits.ftp_use_data_ssl = (data->set.use_ssl != CURLUSESSL_CONTROL); - /* FTP servers typically responds with 500 if they decide to reject - our 'P' request */ - else if(data->set.use_ssl > CURLUSESSL_CONTROL) - /* we failed and bails out */ - return CURLE_USE_SSL_FAILED; + if(result) + ; + else if((data->state.list_only || !ftpc->file) && + !(data->set.prequote)) { + /* The specified path ends with a slash, and therefore we think this + is a directory that is requested, use LIST. But before that we + need to set ASCII transfer mode. */ - if(data->set.ftp_ccc) { - /* CCC - Clear Command Channel - */ - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC"); - if(!result) - ftp_state(data, ftpc, FTP_CCC); - } - else - result = ftp_state_pwd(data, ftpc); - break; + /* But only if a body transfer was requested. */ + if(ftp->transfer == PPTRANSFER_BODY) { + result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE); + if(result) + return result; + } + /* otherwise fall through */ + } + else { + if(data->set.prequote && !ftpc->file) { + result = ftp_nb_type(data, ftpc, ftp, TRUE, + FTP_RETR_LIST_TYPE); + } + else { + result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii, + FTP_RETR_TYPE); + } + if(result) + return result; + } - case FTP_CCC: - if(ftpcode < 500) { - /* First shut down the SSL layer (note: this call will block) */ - /* This has only been tested on the proftpd server, and the mod_tls - * code sends a close notify alert without waiting for a close notify - * alert in response. Thus we wait for a close notify alert from the - * server, but we do not send one. Let's hope other servers do - * the same... */ - result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET, - (data->set.ftp_ccc == - (unsigned char)CURLFTPSSL_CCC_ACTIVE)); - if(result) - failf(data, "Failed to clear the command channel (CCC)"); + result = ftp_statemach(data, ftpc, &complete); + *completep = (int)complete; } - if(!result) - /* Then continue as normal */ - result = ftp_state_pwd(data, ftpc); - break; + return result; + } - case FTP_PWD: - result = ftp_pwd_resp(data, ftpc, ftpcode); - break; + /* no data to transfer */ + Curl_xfer_setup_nop(data); - case FTP_SYST: - if(ftpcode == 215) { - char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first - letter */ - char *os; - char *start; + if(!ftpc->wait_data_conn) { + /* no waiting for the data connection so this is now complete */ + *completep = 1; + CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc), + (int)result); + } - /* Reply format is like - 215 - */ - while(*ptr == ' ') - ptr++; - for(start = ptr; *ptr && *ptr != ' '; ptr++) - ; - os = Curl_memdup0(start, ptr - start); - if(!os) - return CURLE_OUT_OF_MEMORY; + return result; +} - /* Check for special servers here. */ - if(curl_strequal(os, "OS/400")) { - /* Force OS400 name format 1. */ - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1"); - if(result) { - curlx_free(os); - return result; - } - /* remember target server OS */ - curlx_free(ftpc->server_os); - ftpc->server_os = os; - ftp_state(data, ftpc, FTP_NAMEFMT); - break; - } - /* Nothing special for the target server. */ - /* remember target server OS */ - curlx_free(ftpc->server_os); - ftpc->server_os = os; - } - else { - /* Cannot identify server OS. Continue anyway and cross fingers. */ +/* call this when the DO phase has completed */ +static CURLcode ftp_dophase_done(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool connected) +{ + if(connected) { + int completed; + CURLcode result = ftp_do_more(data, &completed); + + if(result) { + close_secondarysocket(data, ftpc); + return result; } + } - ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */ - CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc)); - break; + if(ftp->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + else if(!connected) + /* since we did not connect now, we want do_more to get called */ + data->conn->bits.do_more = TRUE; - case FTP_NAMEFMT: - if(ftpcode == 250) { - /* Name format change successful: reload initial path. */ - ftp_state_pwd(data, ftpc); - break; - } + ftpc->ctl_valid = TRUE; /* seems good */ - ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */ - CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc)); - break; + return CURLE_OK; +} - case FTP_QUOTE: - case FTP_POSTQUOTE: - case FTP_RETR_PREQUOTE: - case FTP_STOR_PREQUOTE: - case FTP_LIST_PREQUOTE: - if((ftpcode >= 400) && !ftpc->count2) { - /* failure response code, and not allowed to fail */ - failf(data, "QUOT command failed with %03d", ftpcode); - result = CURLE_QUOTE_ERROR; +static CURLcode ftp_state_port_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + int ftpcode) +{ + struct connectdata *conn = data->conn; + ftpport fcmd = (ftpport)ftpc->count1; + CURLcode result = CURLE_OK; + + /* The FTP spec tells a positive response should have code 200. + Be more permissive here to tolerate deviant servers. */ + if(ftpcode / 100 != 2) { + /* the command failed */ + + if(EPRT == fcmd) { + infof(data, "disabling EPRT usage"); + conn->bits.ftp_use_eprt = FALSE; + } + fcmd++; + + if(fcmd == DONE) { + failf(data, "Failed to do PORT"); + result = CURLE_FTP_PORT_FAILED; } else - result = ftp_state_quote(data, ftpc, ftp, FALSE, ftpc->state); - break; + /* try next */ + result = ftp_state_use_port(data, ftpc, fcmd); + } + else { + infof(data, "Connect data stream actively"); + ftp_state(data, ftpc, FTP_STOP); /* end of DO phase */ + result = ftp_dophase_done(data, ftpc, ftp, FALSE); + } - case FTP_CWD: - if(ftpcode / 100 != 2) { - /* failure to CWD there */ - if(data->set.ftp_create_missing_dirs && - ftpc->cwdcount && !ftpc->count2) { - /* try making it */ - ftpc->count2++; /* counter to prevent CWD-MKD loops */ + return result; +} - /* count3 is set to allow MKD to fail once per dir. In the case when - CWD fails and then MKD fails (due to another session raced it to - create the dir) this then allows for a second try to CWD to it. */ - ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0; +static int twodigit(const char *p) +{ + return ((p[0] - '0') * 10) + (p[1] - '0'); +} - result = Curl_pp_sendf(data, &ftpc->pp, "MKD %.*s", - pathlen(ftpc, ftpc->cwdcount - 1), - pathpiece(ftpc, ftpc->cwdcount - 1)); - if(!result) - ftp_state(data, ftpc, FTP_MKD); - } - else { - /* return failure */ - failf(data, "Server denied you to change to the given directory"); - ftpc->cwdfail = TRUE; /* do not remember this path as we failed - to enter it */ - result = CURLE_REMOTE_ACCESS_DENIED; - } - } - else { - /* success */ - ftpc->count2 = 0; - if(ftpc->cwdcount >= ftpc->dirdepth) - result = ftp_state_mdtm(data, ftpc, ftp); - else { - ftpc->cwdcount++; - /* send next CWD */ - result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s", - pathlen(ftpc, ftpc->cwdcount - 1), - pathpiece(ftpc, ftpc->cwdcount - 1)); - } +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 client_write_header(struct Curl_easy *data, + char *buf, size_t blen) +{ + /* Some replies from an FTP server are written to the client + * as CLIENTWRITE_HEADER, formatted as if they came from a + * HTTP conversation. + * In all protocols, CLIENTWRITE_HEADER data is only passed to + * the body write callback when data->set.include_header is set + * via CURLOPT_HEADER. + * For historic reasons, FTP never played this game and expects + * all its headers to do that always. Set that flag during the + * call to Curl_client_write() so it does the right thing. + * + * Notice that we cannot enable this flag for FTP in general, + * as an FTP transfer might involve an HTTP proxy connection and + * headers from CONNECT should not automatically be part of the + * output. */ + CURLcode result; + bool save = (bool)data->set.include_header; + data->set.include_header = TRUE; + result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen); + data->set.include_header = save; + return result; +} + +static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + int ftpcode) +{ + CURLcode result = CURLE_OK; + + switch(ftpcode) { + case 213: { + /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the + last .sss part is optional and means fractions of a second */ + int year, month, day, hour, minute, second; + struct pingpong *pp = &ftpc->pp; + const char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4; + bool showtime = FALSE; + if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) { + /* we have a time, reformat it */ + char timebuf[24]; + curl_msnprintf(timebuf, sizeof(timebuf), + "%04d%02d%02d %02d:%02d:%02d GMT", + year, month, day, hour, minute, second); + /* now, convert this into a time() value: */ + if(!Curl_getdate_capped(timebuf, &data->info.filetime)) + showtime = TRUE; } + + /* If we asked for a time of the file and we actually got one as well, + we "emulate" an HTTP-style header in our output. */ + +#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) +#pragma GCC diagnostic push +/* 'time_t' is unsigned in MSDOS and AmigaOS. Silence: + warning: comparison of unsigned expression in '>= 0' is always true */ +#pragma GCC diagnostic ignored "-Wtype-limits" +#endif + if(data->req.no_body && ftpc->file && + data->set.get_filetime && showtime) { +#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) +#pragma GCC diagnostic pop +#endif + char headerbuf[128]; + int headerbuflen; + time_t filetime = data->info.filetime; + struct tm buffer; + const struct tm *tm = &buffer; + + result = curlx_gmtime(filetime, &buffer); + if(result) + return result; + + /* format: "Tue, 15 Nov 1994 12:45:26" */ + headerbuflen = + curl_msnprintf(headerbuf, sizeof(headerbuf), + "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d " + "GMT\r\n", + Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6], + tm->tm_mday, + Curl_month[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + result = client_write_header(data, headerbuf, headerbuflen); + if(result) + return result; + } /* end of a ridiculous amount of conditionals */ + } break; + default: + infof(data, "unsupported MDTM reply format"); + break; + case 550: /* 550 is used for several different problems, e.g. + "No such file or directory" or "Permission denied". + It does not mean that the file does not exist at all. */ + infof(data, "MDTM failed: file does not exist or permission problem," + " continuing"); + break; + } - case FTP_MKD: - if((ftpcode / 100 != 2) && !ftpc->count3--) { - /* failure to MKD the directory */ - failf(data, "Failed to MKD dir: %03d", ftpcode); - result = CURLE_REMOTE_ACCESS_DENIED; + if(data->set.timecondition) { + if((data->info.filetime > 0) && (data->set.timevalue > 0)) { + switch(data->set.timecondition) { + case CURL_TIMECOND_IFMODSINCE: + default: + if(data->info.filetime <= data->set.timevalue) { + infof(data, "The requested document is not new enough"); + ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ + data->info.timecond = TRUE; + ftp_state(data, ftpc, FTP_STOP); + return CURLE_OK; + } + break; + case CURL_TIMECOND_IFUNMODSINCE: + if(data->info.filetime > data->set.timevalue) { + infof(data, "The requested document is not old enough"); + ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ + data->info.timecond = TRUE; + ftp_state(data, ftpc, FTP_STOP); + return CURLE_OK; + } + break; + } /* switch */ } else { - ftp_state(data, ftpc, FTP_CWD); - /* send CWD */ - result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s", - pathlen(ftpc, ftpc->cwdcount - 1), - pathpiece(ftpc, ftpc->cwdcount - 1)); + infof(data, "Skipping time comparison"); } - break; - - case FTP_MDTM: - result = ftp_state_mdtm_resp(data, ftpc, ftp, ftpcode); - break; + } - case FTP_TYPE: - case FTP_LIST_TYPE: - case FTP_RETR_TYPE: - case FTP_STOR_TYPE: - case FTP_RETR_LIST_TYPE: - result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state); - break; + if(!result) + result = ftp_state_type(data, ftpc, ftp); - case FTP_SIZE: - case FTP_RETR_SIZE: - case FTP_STOR_SIZE: - result = ftp_state_size_resp(data, ftpc, ftp, ftpcode, ftpc->state); - break; + return result; +} - case FTP_REST: - case FTP_RETR_REST: - result = ftp_state_rest_resp(data, ftpc, ftp, ftpcode, ftpc->state); - break; +static CURLcode ftp_state_type_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; - case FTP_PRET: - if(ftpcode != 200) { - /* there only is this one standard OK return code. */ - failf(data, "PRET command not accepted: %03d", ftpcode); - return CURLE_FTP_PRET_FAILED; - } - result = ftp_state_use_pasv(data, ftpc, conn); - break; + if(ftpcode / 100 != 2) { + /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a + successful 'TYPE I'. While that is not as RFC959 says, it is still a + positive response code and we allow that. */ + failf(data, "Could not set desired mode"); + return CURLE_FTP_COULDNT_SET_TYPE; + } + if(ftpcode != 200) + infof(data, "Got a %03d response code instead of the assumed 200", + ftpcode); - case FTP_PASV: - result = ftp_state_pasv_resp(data, ftpc, ftpcode); - break; + if(instate == FTP_TYPE) + result = ftp_state_size(data, ftpc, ftp); + else if(instate == FTP_LIST_TYPE) + result = ftp_state_list(data, ftpc, ftp); + else if(instate == FTP_RETR_TYPE) + result = ftp_state_retr_prequote(data, ftpc, ftp); + else if(instate == FTP_STOR_TYPE) + result = ftp_state_stor_prequote(data, ftpc, ftp); + else if(instate == FTP_RETR_LIST_TYPE) + result = ftp_state_list_prequote(data, ftpc, ftp); - case FTP_PORT: - result = ftp_state_port_resp(data, ftpc, ftp, ftpcode); - break; + return result; +} - case FTP_LIST: - case FTP_RETR: - result = ftp_state_get_resp(data, ftpc, ftp, ftpcode, ftpc->state); - break; +static CURLcode ftp_state_size_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; + curl_off_t filesize = -1; + const char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); + size_t len = ftpc->pp.nfinal; - case FTP_STOR: - result = ftp_state_stor_resp(data, ftpc, ftpcode); - break; + /* get the size from the ascii string: */ + if(ftpcode == 213) { + /* To allow servers to prepend "rubbish" in the response string, we scan + for all the digits at the end of the response and parse only those as a + number. */ + const char *start = &buf[4]; + const char *fdigit = memchr(start, '\r', len - 4); + if(fdigit) { + fdigit--; + if(*fdigit == '\n') + fdigit--; + while(ISDIGIT(fdigit[-1]) && (fdigit > start)) + fdigit--; + } + else + fdigit = start; + if(curlx_str_number(&fdigit, &filesize, CURL_OFF_T_MAX)) + filesize = -1; /* size remain unknown */ + } + else if(ftpcode == 550) { /* "No such file or directory" */ + /* allow a SIZE failure for (resumed) uploads, when probing what command + to use */ + if(instate != FTP_STOR_SIZE) { + failf(data, "The file does not exist"); + return CURLE_REMOTE_FILE_NOT_FOUND; + } + } - case FTP_QUIT: - default: - /* internal error */ - ftp_state(data, ftpc, FTP_STOP); - break; + if(instate == FTP_SIZE) { + if(filesize != -1) { + char clbuf[128]; + int clbuflen = curl_msnprintf(clbuf, sizeof(clbuf), + "Content-Length: %" FMT_OFF_T "\r\n", + filesize); + result = client_write_header(data, clbuf, clbuflen); + if(result) + return result; + } + Curl_pgrsSetDownloadSize(data, filesize); + result = ftp_state_rest(data, ftpc, ftp); + } + else if(instate == FTP_RETR_SIZE) { + Curl_pgrsSetDownloadSize(data, filesize); + result = ftp_state_retr(data, ftpc, ftp, filesize); + } + else if(instate == FTP_STOR_SIZE) { + data->state.resume_from = filesize; + result = ftp_state_ul_setup(data, ftpc, ftp, TRUE); } return result; } -/* called repeatedly until done from multi.c */ -static CURLcode ftp_statemach(struct Curl_easy *data, - struct ftp_conn *ftpc, - bool *done) +static CURLcode ftp_state_rest_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + int ftpcode, + ftpstate instate) { - CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); + CURLcode result = CURLE_OK; - /* Check for the state outside of the Curl_socket_check() return code checks - since at times we are in fact already in this state when this function - gets called. */ - *done = (ftpc->state == FTP_STOP); + switch(instate) { + case FTP_REST: + default: + if(ftpcode == 350) { + char buffer[24] = { "Accept-ranges: bytes\r\n" }; + result = client_write_header(data, buffer, strlen(buffer)); + if(result) + return result; + } + result = ftp_state_prepare_transfer(data, ftpc, ftp); + break; - return result; -} + case FTP_RETR_REST: + if(ftpcode != 350) { + failf(data, "Could not use REST"); + result = CURLE_FTP_COULDNT_USE_REST; + } + else { + result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_RETR); + } + break; + } -/* called repeatedly until done from multi.c */ -static CURLcode ftp_multi_statemach(struct Curl_easy *data, - bool *done) -{ - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - return ftpc ? ftp_statemach(data, ftpc, done) : CURLE_FAILED_INIT; + return result; } -static CURLcode ftp_block_statemach(struct Curl_easy *data, - struct ftp_conn *ftpc) +static CURLcode ftp_state_stor_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + int ftpcode) { - struct pingpong *pp = &ftpc->pp; CURLcode result = CURLE_OK; - while(ftpc->state != FTP_STOP) { - if(ftpc->shutdown) - CURL_TRC_FTP(data, "in shutdown, waiting for server response"); - result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */); - if(result) - break; + if(ftpcode >= 400) { + failf(data, "Failed FTP upload: %0d", ftpcode); + ftp_state(data, ftpc, FTP_STOP); + return CURLE_UPLOAD_FAILED; } - return result; -} - -/* - * ftp_connect() should do everything that is to be considered a part of - * the connection phase. - * - * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE if not. - * - */ -static CURLcode ftp_connect(struct Curl_easy *data, - bool *done) /* see description above */ -{ - CURLcode result; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - struct pingpong *pp; + /* PORT means we are now awaiting the server to connect to us. */ + if(data->set.ftp_use_port) { + bool connected; - *done = FALSE; /* default to not done yet */ - if(!ftpc) - return CURLE_FAILED_INIT; - pp = &ftpc->pp; - PINGPONG_SETUP(pp, ftp_pp_statemachine, ftp_endofresp); + ftp_state(data, ftpc, FTP_STOP); /* no longer in STOR state */ - if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - /* BLOCKING */ - result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); if(result) return result; - conn->bits.ftp_use_control_ssl = TRUE; + + if(!connected) { + infof(data, "Data conn was not available immediately"); + ftpc->wait_data_conn = TRUE; + return ftp_check_ctrl_on_data_wait(data, ftpc); + } + ftpc->wait_data_conn = FALSE; } + return ftp_initiate_transfer(data, ftpc); +} - Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */ +/* for LIST and RETR responses */ +static CURLcode ftp_state_get_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + int ftpcode, + ftpstate instate) +{ + CURLcode result = CURLE_OK; - /* When we connect, we start in the state where we await the 220 - response */ - ftp_state(data, ftpc, FTP_WAIT220); + if((ftpcode == 150) || (ftpcode == 125)) { - result = ftp_statemach(data, ftpc, done); + /* + A; + 150 Opening BINARY mode data connection for /etc/passwd (2241 + bytes). (ok, the file is being transferred) - return result; -} + B: + 150 Opening ASCII mode data connection for /bin/ls -/*********************************************************************** - * - * ftp_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - struct connectdata *conn = data->conn; - struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - struct pingpong *pp; - size_t nread; - int ftpcode; - CURLcode result = CURLE_OK; + C: + 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes). - if(!ftp || !ftpc) - return CURLE_OK; + D: + 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes) - pp = &ftpc->pp; - switch(status) { - case CURLE_BAD_DOWNLOAD_RESUME: - case CURLE_FTP_WEIRD_PASV_REPLY: - case CURLE_FTP_PORT_FAILED: - case CURLE_FTP_ACCEPT_FAILED: - case CURLE_FTP_ACCEPT_TIMEOUT: - case CURLE_FTP_COULDNT_SET_TYPE: - case CURLE_FTP_COULDNT_RETR_FILE: - case CURLE_PARTIAL_FILE: - case CURLE_UPLOAD_FAILED: - case CURLE_REMOTE_ACCESS_DENIED: - case CURLE_FILESIZE_EXCEEDED: - case CURLE_REMOTE_FILE_NOT_FOUND: - case CURLE_WRITE_ERROR: - /* the connection stays alive fine even though this happened */ - case CURLE_OK: /* does not affect the control connection's status */ - if(!premature) - break; + E: + 125 Data connection already open; Transfer starting. */ - /* until we cope better with prematurely ended requests, let them - * fallback as if in complete failure */ - FALLTHROUGH(); - default: /* by default, an error means the control connection is - wedged and should not be used anymore */ - ftpc->ctl_valid = FALSE; - ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the - current path, as this connection is going */ - connclose(conn, "FTP ended with bad error code"); - result = status; /* use the already set error code */ - break; - } + data->req.size = -1; /* default unknown size */ - if(data->state.wildcardmatch) { - if(data->set.chunk_end && ftpc->file) { - Curl_set_in_callback(data, TRUE); - data->set.chunk_end(data->set.wildcardptr); - Curl_set_in_callback(data, FALSE); - freedirs(ftpc); + /* + * It appears that there are FTP-servers that return size 0 for files when + * SIZE is used on the file while being in BINARY mode. To work around + * that (stupid) behavior, we attempt to parse the RETR response even if + * the SIZE returned size zero. + * + * Debugging help from Salvatore Sorrentino on February 26, 2003. + */ + + if((instate != FTP_LIST) && + !data->state.prefer_ascii && + !data->set.ignorecl && + (ftp->downloadsize < 1)) { + /* + * It seems directory listings either do not show the size or often uses + * size 0 anyway. ASCII transfers may cause that the transferred amount + * of data is not the same as this line tells, why using this number in + * those cases only confuses us. + * + * Example D above makes this parsing a little tricky */ + size_t len = curlx_dyn_len(&ftpc->pp.recvbuf); + if(len >= 7) { /* "1 bytes" is 7 characters */ + size_t i; + for(i = 0; i < len - 7; i++) { + curl_off_t what; + const char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); + const char *c = &buf[i]; + if(!curlx_str_number(&c, &what, CURL_OFF_T_MAX) && + !curlx_str_single(&c, ' ') && + !strncmp(c, "bytes", 5)) { + data->req.size = what; + break; + } + } + } } - ftpc->known_filesize = -1; - } + else if(ftp->downloadsize > -1) + data->req.size = ftp->downloadsize; - if(result) { - /* We can limp along anyway (and should try to since we may already be in - * the error path) */ - ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ - curlx_free(ftpc->prevpath); - ftpc->prevpath = NULL; /* no path remembering */ - } - else { /* remember working directory for connection reuse */ - const char *rawPath = ftpc->rawpath; - if(rawPath) { - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) - ; /* full path => no CWDs happened => keep ftpc->prevpath */ - else { - size_t pathLen = strlen(ftpc->rawpath); + if(data->req.size > data->req.maxdownload && data->req.maxdownload > 0) + data->req.size = data->req.maxdownload; + else if((instate != FTP_LIST) && (data->state.prefer_ascii)) + data->req.size = -1; /* for servers that understate ASCII mode file + size */ - curlx_free(ftpc->prevpath); + infof(data, "Maxdownload = %" FMT_OFF_T, data->req.maxdownload); - if(!ftpc->cwdfail) { - if(data->set.ftp_filemethod == FTPFILE_NOCWD) - pathLen = 0; /* relative path => working directory is FTP home */ - else - /* file is url-decoded */ - pathLen -= ftpc->file ? strlen(ftpc->file) : 0; - ftpc->prevpath = Curl_memdup0(rawPath, pathLen); - } - else - ftpc->prevpath = NULL; /* no path */ - } - } - if(ftpc->prevpath) - infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath); - } + if(instate != FTP_LIST) + infof(data, "Getting file with size: %" FMT_OFF_T, data->req.size); - /* shut down the socket to inform the server we are done */ + if(data->set.ftp_use_port) { + bool connected; - if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) { - if(!result && ftpc->dont_check && data->req.maxdownload > 0) { - /* partial download completed */ - result = Curl_pp_sendf(data, pp, "%s", "ABOR"); - if(result) { - failf(data, "Failure sending ABOR command: %s", - curl_easy_strerror(result)); - ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "ABOR command failed"); /* connection closure */ + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); + if(result) + return result; + + if(!connected) { + infof(data, "Data conn was not available immediately"); + ftp_state(data, ftpc, FTP_STOP); + ftpc->wait_data_conn = TRUE; + return ftp_check_ctrl_on_data_wait(data, ftpc); } + ftpc->wait_data_conn = FALSE; + } + return ftp_initiate_transfer(data, ftpc); + } + else { + if((instate == FTP_LIST) && (ftpcode == 450)) { + /* no matching files in the directory listing */ + ftp->transfer = PPTRANSFER_NONE; /* do not download anything */ + ftp_state(data, ftpc, FTP_STOP); /* this phase is over */ + } + else { + failf(data, "RETR response: %03d", ftpcode); + return instate == FTP_RETR && ftpcode == 550 ? + CURLE_REMOTE_FILE_NOT_FOUND : + CURLE_FTP_COULDNT_RETR_FILE; } - - close_secondarysocket(data, ftpc); } - if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && - pp->pending_resp && !premature) { - /* - * Let's see what the server says about the transfer we just performed, - * but lower the timeout as sometimes this connection has died while the - * data has been transferred. This happens when doing through NATs etc that - * abandon old silent connections. - */ - pp->response = *Curl_pgrs_now(data); /* timeout relative now */ - result = getftpresponse(data, &nread, &ftpcode); + return result; +} - if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) { - failf(data, "control connection looks dead"); - ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */ - } +/* after USER, PASS and ACCT */ +static CURLcode ftp_state_loggedin(struct Curl_easy *data, + struct ftp_conn *ftpc) +{ + CURLcode result = CURLE_OK; - if(result) - return result; + if(data->conn->bits.ftp_use_control_ssl) { + /* PBSZ = PROTECTION BUFFER SIZE. - if(ftpc->dont_check && data->req.maxdownload > 0) { - /* we have just sent ABOR and there is no reliable way to check if it was - * successful or not; we have to close the connection now */ - infof(data, "partial download completed, closing connection"); - connclose(conn, "Partial download with no ability to check"); - return result; - } + The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: - if(!ftpc->dont_check) { - /* 226 Transfer complete, 250 Requested file action okay, completed. */ - switch(ftpcode) { - case 226: - case 250: - break; - case 552: - failf(data, "Exceeded storage allocation"); - result = CURLE_REMOTE_DISK_FULL; - break; - default: - failf(data, "server did not report OK, got %d", ftpcode); - result = CURLE_PARTIAL_FILE; - break; - } - } + Specifically, the PROT command MUST be preceded by a PBSZ + command and a PBSZ command MUST be preceded by a successful + security data exchange (the TLS negotiation in this case) + + ... (and on page 8): + + Thus the PBSZ command must still be issued, but must have a + parameter of '0' to indicate that no buffering is taking place + and the data connection should not be encapsulated. + */ + result = Curl_pp_sendf(data, &ftpc->pp, "PBSZ %d", 0); + if(!result) + ftp_state(data, ftpc, FTP_PBSZ); + } + else { + result = ftp_state_pwd(data, ftpc); } + return result; +} - if(result || premature) - /* the response code from the transfer showed an error already so no - use checking further */ - ; - else if(data->state.upload) { - if((ftp->transfer == PPTRANSFER_BODY) && - (data->state.infilesize != -1) && /* upload with known size */ - ((!data->set.crlf && !data->state.prefer_ascii && /* no conversion */ - (data->state.infilesize != data->req.writebytecount)) || - ((data->set.crlf || data->state.prefer_ascii) && /* maybe crlf conv */ - (data->state.infilesize > data->req.writebytecount)) - )) { - failf(data, "Uploaded unaligned file size (%" FMT_OFF_T - " out of %" FMT_OFF_T " bytes)", - data->req.writebytecount, data->state.infilesize); - result = CURLE_PARTIAL_FILE; +/* for USER and PASS responses */ +static CURLcode ftp_state_user_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + int ftpcode) +{ + CURLcode result = CURLE_OK; + + /* some need password anyway, and others return 2xx ignored */ + if((ftpcode == 331) && (ftpc->state == FTP_USER)) { + /* 331 Password required for ... + (the server requires to send the user's password too) */ + result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", data->conn->passwd); + if(!result) + ftp_state(data, ftpc, FTP_PASS); + } + else if(ftpcode / 100 == 2) { + /* 230 User ... logged in. + (the user logged in with or without password) */ + result = ftp_state_loggedin(data, ftpc); + } + else if(ftpcode == 332) { + if(data->set.str[STRING_FTP_ACCOUNT]) { + result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", + data->set.str[STRING_FTP_ACCOUNT]); + if(!result) + ftp_state(data, ftpc, FTP_ACCT); + } + else { + failf(data, "ACCT requested but none available"); + result = CURLE_LOGIN_DENIED; } } else { - if((data->req.size != -1) && - (data->req.size != data->req.bytecount) && - (data->req.maxdownload != data->req.bytecount)) { - failf(data, "Received only partial file: %" FMT_OFF_T " bytes", - data->req.bytecount); - result = CURLE_PARTIAL_FILE; + /* All other response codes, like: + + 530 User ... access denied + (the server denies to log the specified user) */ + + if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] && + !ftpc->ftp_trying_alternative) { + /* Ok, USER failed. Let's try the supplied command. */ + result = Curl_pp_sendf(data, &ftpc->pp, "%s", + data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); + if(!result) { + ftpc->ftp_trying_alternative = TRUE; + ftp_state(data, ftpc, FTP_USER); + } } - else if(!ftpc->dont_check && - !data->req.bytecount && - (data->req.size > 0)) { - failf(data, "No data was received"); - result = CURLE_FTP_COULDNT_RETR_FILE; + else { + failf(data, "Access denied: %03d", ftpcode); + result = CURLE_LOGIN_DENIED; } } + return result; +} - /* clear these for next connection */ - ftp->transfer = PPTRANSFER_BODY; - ftpc->dont_check = FALSE; +/* for ACCT response */ +static CURLcode ftp_state_acct_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + int ftpcode) +{ + CURLcode result = CURLE_OK; + if(ftpcode != 230) { + failf(data, "ACCT rejected by server: %03d", ftpcode); + result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */ + } + else + result = ftp_state_loggedin(data, ftpc); - /* Send any post-transfer QUOTE strings? */ - if(!status && !result && !premature && data->set.postquote) - result = ftp_sendquote(data, ftpc, data->set.postquote); - CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), result); return result; } -/*********************************************************************** - * - * ftp_sendquote() - * - * Where a 'quote' means a list of custom commands to send to the server. - * The quote list is passed as an argument. - * - * BLOCKING - */ -static CURLcode ftp_sendquote(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct curl_slist *quote) +static CURLcode ftp_pwd_resp(struct Curl_easy *data, + struct ftp_conn *ftpc, + int ftpcode) { - struct curl_slist *item; struct pingpong *pp = &ftpc->pp; + CURLcode result; + + if(ftpcode == 257) { + const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ + bool entry_extracted = FALSE; + struct dynbuf out; + curlx_dyn_init(&out, 1000); - item = quote; - while(item) { - if(item->data) { - size_t nread; - char *cmd = item->data; - bool acceptfail = FALSE; - CURLcode result; - int ftpcode = 0; + /* Reply format is like + 257[rubbish]"" and the + RFC959 says - /* if a command starts with an asterisk, which a legal FTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server responds. */ + The directory name can contain any character; embedded + double-quotes should be escaped by double-quotes (the + "quote-doubling" convention). + */ - if(cmd[0] == '*') { - cmd++; - acceptfail = TRUE; + /* scan for the first double-quote for non-standard responses */ + while(*ptr != '\n' && *ptr != '\0' && *ptr != '"') + ptr++; + + if('\"' == *ptr) { + /* it started good */ + for(ptr++; *ptr; ptr++) { + if('\"' == *ptr) { + if('\"' == ptr[1]) { + /* "quote-doubling" */ + result = curlx_dyn_addn(&out, &ptr[1], 1); + ptr++; + } + else { + /* end of path */ + if(curlx_dyn_len(&out)) + entry_extracted = TRUE; + break; /* get out of this loop */ + } + } + else + result = curlx_dyn_addn(&out, ptr, 1); + if(result) + return result; } + } + if(entry_extracted) { + /* If the path name does not look like an absolute path (i.e.: it + does not start with a '/'), we probably need some server-dependent + adjustments. For example, this is the case when connecting to + an OS400 FTP server: this server supports two name syntaxes, + the default one being incompatible with standard paths. In + addition, this server switches automatically to the regular path + syntax when one is encountered in a command: this results in + having an entrypath in the wrong syntax when later used in CWD. + The method used here is to check the server OS: we do it only + if the path name looks strange to minimize overhead on other + systems. */ + char *dir = curlx_dyn_ptr(&out); - result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); - if(!result) { - pp->response = *Curl_pgrs_now(data); /* timeout relative now */ - result = getftpresponse(data, &nread, &ftpcode); + if(!ftpc->server_os && dir[0] != '/') { + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST"); + if(result) { + curlx_dyn_free(&out); + return result; + } } - if(result) - return result; - if(!acceptfail && (ftpcode >= 400)) { - failf(data, "QUOT string not accepted: %s", cmd); - return CURLE_QUOTE_ERROR; + curlx_free(ftpc->entrypath); + ftpc->entrypath = dir; /* remember this */ + infof(data, "Entry path is '%s'", ftpc->entrypath); + /* also save it where getinfo can access it: */ + curlx_free(data->state.most_recent_ftp_entrypath); + data->state.most_recent_ftp_entrypath = curlx_strdup(ftpc->entrypath); + if(!data->state.most_recent_ftp_entrypath) + return CURLE_OUT_OF_MEMORY; + + if(!ftpc->server_os && dir[0] != '/') { + ftp_state(data, ftpc, FTP_SYST); + return CURLE_OK; } } - - item = item->next; + else { + /* could not get the path */ + curlx_dyn_free(&out); + infof(data, "Failed to figure out path"); + } } - + ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */ + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc)); return CURLE_OK; } -/*********************************************************************** - * - * ftp_need_type() - * - * Returns TRUE if we in the current situation should send TYPE - */ -static int ftp_need_type(struct ftp_conn *ftpc, - bool ascii_wanted) -{ - return ftpc->transfertype != (ascii_wanted ? 'A' : 'I'); -} +static const char * const ftpauth[] = { "SSL", "TLS" }; -/*********************************************************************** - * - * ftp_nb_type() - * - * Set TYPE. We only deal with ASCII or BINARY so this function - * sets one of them. - * If the transfer type is not sent, simulate on OK response in newstate - */ -static CURLcode ftp_nb_type(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool ascii, ftpstate newstate) +static CURLcode ftp_wait_resp(struct Curl_easy *data, + struct connectdata *conn, + struct ftp_conn *ftpc, + int ftpcode) { - CURLcode result; - char want = (char)(ascii ? 'A' : 'I'); - - if(ftpc->transfertype == want) { - ftp_state(data, ftpc, newstate); - return ftp_state_type_resp(data, ftpc, ftp, 200, newstate); + CURLcode result = CURLE_OK; + if(ftpcode == 230) { + /* 230 User logged in - already! Take as 220 if TLS required. */ + if(data->set.use_ssl <= CURLUSESSL_TRY || + conn->bits.ftp_use_control_ssl) + return ftp_state_user_resp(data, ftpc, ftpcode); + } + else if(ftpcode != 220) { + failf(data, "Got a %03d ftp-server response when 220 was expected", + ftpcode); + return CURLE_WEIRD_SERVER_REPLY; } - result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want); - if(!result) { - ftp_state(data, ftpc, newstate); + if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) { + /* We do not have an SSL/TLS control connection yet, but FTPS is + requested. Try an FTPS connection now */ - /* keep track of our current transfer type */ - ftpc->transfertype = want; + ftpc->count3 = 0; + switch((long)data->set.ftpsslauth) { + case CURLFTPAUTH_DEFAULT: + case CURLFTPAUTH_SSL: + ftpc->count2 = 1; /* add one to get next */ + ftpc->count1 = 0; + break; + case CURLFTPAUTH_TLS: + ftpc->count2 = -1; /* subtract one to get next */ + ftpc->count1 = 1; + break; + default: + failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d", + (int)data->set.ftpsslauth); + return CURLE_UNKNOWN_OPTION; /* we do not know what to do */ + } + result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]); + if(!result) + ftp_state(data, ftpc, FTP_AUTH); } + else + result = ftp_state_user(data, ftpc, conn); return result; } -/*************************************************************************** - * - * ftp_pasv_verbose() - * - * This function only outputs some informationals about this second connection - * when we have issued a PASV command before and thus we have connected to a - * possibly new IP address. - * - */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void -ftp_pasv_verbose(struct Curl_easy *data, - struct Curl_addrinfo *ai, - char *newhost, /* ASCII version */ - int port) -{ - char buf[256]; - Curl_printable_address(ai, buf, sizeof(buf)); - infof(data, "Connecting to %s (%s) port %d", newhost, buf, port); -} -#endif - -/* - * ftp_do_more() - * - * This function shall be called when the second FTP (data) connection is - * connected. - * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back - * (which basically is only for when PASV is being sent to retry a failed - * EPSV). - */ -static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) +static CURLcode ftp_pp_statemachine(struct Curl_easy *data, + struct connectdata *conn) { - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + CURLcode result; + int ftpcode; + struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); - CURLcode result = CURLE_OK; - bool connected = FALSE; - bool complete = FALSE; - /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP - * proxy then the state will not be valid until after that connection is - * complete */ + struct pingpong *pp; + size_t nread = 0; if(!ftpc || !ftp) return CURLE_FAILED_INIT; + pp = &ftpc->pp; + if(pp->sendleft) + return Curl_pp_flushsend(data, pp); - *completep = 0; /* default to stay in the state */ - - /* if the second connection has been set up, try to connect it fully - * to the remote host. This may not complete at this time, for several - * reasons: - * - we do EPTR and the server will not connect to our listen socket - * until we send more FTP commands - * - an SSL filter is in place and the server will not start the TLS - * handshake until we send more FTP commands - */ - if(conn->cfilter[SECONDARYSOCKET]) { - bool is_eptr = Curl_conn_is_tcp_listen(data, SECONDARYSOCKET); - result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); - if(result == CURLE_OUT_OF_MEMORY) - return result; - if(result || (!connected && !is_eptr && - !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) { - if(result && !is_eptr && (ftpc->count1 == 0)) { - *completep = -1; /* go back to DOING please */ - /* this is a EPSV connect failing, try PASV instead */ - return ftp_epsv_disable(data, ftpc, conn); - } - return result; - } - } - - if(ftpc->state) { - /* already in a state so skip the initial commands. - They are only done to kickstart the do_more state */ - result = ftp_statemach(data, ftpc, &complete); - - *completep = (int)complete; - - /* if we got an error or if we do not wait for a data connection return - immediately */ - if(result || !ftpc->wait_data_conn) - return result; - - /* if we reach the end of the FTP state machine here, *complete will be - TRUE but so is ftpc->wait_data_conn, which says we need to wait for the - data connection and therefore we are not actually complete */ - *completep = 0; - } - - if(ftp->transfer <= PPTRANSFER_INFO) { - /* a transfer is about to take place, or if not a filename was given so we - will do a SIZE on it later and then we need the right TYPE first */ + result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, &ftpcode, &nread); + if(result || !ftpcode) + return result; - if(ftpc->wait_data_conn) { - bool serv_conned; + /* we have now received a full FTP server response */ + switch(ftpc->state) { + case FTP_WAIT220: + result = ftp_wait_resp(data, conn, ftpc, ftpcode); + break; - result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned); - if(result) - return result; /* Failed to accept data connection */ + case FTP_AUTH: + /* we have gotten the response to a previous AUTH command */ - if(serv_conned) { - /* It looks data connection is established */ - ftpc->wait_data_conn = FALSE; - result = ftp_initiate_transfer(data, ftpc); + if(pp->overflow) + return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */ - if(result) - return result; + /* RFC2228 (page 5) says: + * + * If the server is willing to accept the named security mechanism, + * and does not require any security data, it must respond with + * reply code 234/334. + */ - *completep = 1; /* this state is now complete when the server has - connected back to us */ + if((ftpcode == 234) || (ftpcode == 334)) { + /* this was BLOCKING, keep it so for now */ + bool done; + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + if(result) { + /* we failed and bail out */ + return CURLE_USE_SSL_FAILED; + } } - else { - result = ftp_check_ctrl_on_data_wait(data, ftpc); - if(result) - return result; + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done); + if(!result) { + conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ + conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ + result = ftp_state_user(data, ftpc, conn); } } - else if(data->state.upload) { - result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, - FTP_STOR_TYPE); - if(result) - return result; - - result = ftp_statemach(data, ftpc, &complete); - /* ftp_nb_type() might have skipped sending `TYPE A|I` when not - * deemed necessary and directly sent `STORE name`. If this was - * then complete, but we are still waiting on the data connection, - * the transfer has not been initiated yet. */ - *completep = (int)(ftpc->wait_data_conn ? 0 : complete); + else if(ftpc->count3 < 1) { + ftpc->count3++; + ftpc->count1 += ftpc->count2; /* get next attempt */ + result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", + ftpauth[ftpc->count1]); + /* remain in this same state */ } else { - /* download */ - ftp->downloadsize = -1; /* unknown as of yet */ + if(data->set.use_ssl > CURLUSESSL_TRY) + /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */ + result = CURLE_USE_SSL_FAILED; + else + /* ignore the failure and continue */ + result = ftp_state_user(data, ftpc, conn); + } + break; - result = Curl_range(data); + case FTP_USER: + case FTP_PASS: + result = ftp_state_user_resp(data, ftpc, ftpcode); + break; - if(result == CURLE_OK && data->req.maxdownload >= 0) { - /* Do not check for successful transfer */ - ftpc->dont_check = TRUE; - } + case FTP_ACCT: + result = ftp_state_acct_resp(data, ftpc, ftpcode); + break; - if(result) - ; - else if((data->state.list_only || !ftpc->file) && - !(data->set.prequote)) { - /* The specified path ends with a slash, and therefore we think this - is a directory that is requested, use LIST. But before that we - need to set ASCII transfer mode. */ + case FTP_PBSZ: + result = + Curl_pp_sendf(data, &ftpc->pp, "PROT %c", + data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); + if(!result) + ftp_state(data, ftpc, FTP_PROT); + break; - /* But only if a body transfer was requested. */ - if(ftp->transfer == PPTRANSFER_BODY) { - result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE); - if(result) - return result; - } - /* otherwise just fall through */ - } - else { - if(data->set.prequote && !ftpc->file) { - result = ftp_nb_type(data, ftpc, ftp, TRUE, - FTP_RETR_LIST_TYPE); - } - else { - result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, - FTP_RETR_TYPE); - } - if(result) - return result; - } + case FTP_PROT: + if(ftpcode / 100 == 2) + /* We have enabled SSL for the data connection! */ + conn->bits.ftp_use_data_ssl = (data->set.use_ssl != CURLUSESSL_CONTROL); + /* FTP servers typically responds with 500 if they decide to reject + our 'P' request */ + else if(data->set.use_ssl > CURLUSESSL_CONTROL) + /* we failed and bails out */ + return CURLE_USE_SSL_FAILED; - result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; + if(data->set.ftp_ccc) { + /* CCC - Clear Command Channel + */ + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC"); + if(!result) + ftp_state(data, ftpc, FTP_CCC); } - return result; - } - - /* no data to transfer */ - Curl_xfer_setup_nop(data); - - if(!ftpc->wait_data_conn) { - /* no waiting for the data connection so this is now complete */ - *completep = 1; - CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc), - (int)result); - } - - return result; -} - -/*********************************************************************** - * - * ftp_perform() - * - * This is the actual DO function for FTP. Get a file/directory according to - * the options previously setup. - */ -static -CURLcode ftp_perform(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool *connected, /* connect status after PASV / PORT */ - bool *dophase_done) -{ - /* this is FTP and no proxy */ - CURLcode result = CURLE_OK; + else + result = ftp_state_pwd(data, ftpc); + break; - CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_CSTATE(ftpc)); + case FTP_CCC: + if(ftpcode < 500) { + /* First shut down the SSL layer (note: this call will block) */ + /* This has only been tested on the proftpd server, and the mod_tls + * code sends a close notify alert without waiting for a close notify + * alert in response. Thus we wait for a close notify alert from the + * server, but we do not send one. Let's hope other servers do + * the same... */ + result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET, + (data->set.ftp_ccc == + (unsigned char)CURLFTPSSL_CCC_ACTIVE)); + if(result) + failf(data, "Failed to clear the command channel (CCC)"); + } + if(!result) + /* Then continue as normal */ + result = ftp_state_pwd(data, ftpc); + break; - if(data->req.no_body) { - /* requested no body means no transfer... */ - ftp->transfer = PPTRANSFER_INFO; - } + case FTP_PWD: + result = ftp_pwd_resp(data, ftpc, ftpcode); + break; - *dophase_done = FALSE; /* not done yet */ + case FTP_SYST: + if(ftpcode == 215) { + const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ + const char *start; + char *os; - /* start the first command in the DO phase */ - result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_QUOTE); - if(result) - return result; + /* Reply format is like + 215 + */ + while(*ptr == ' ') + ptr++; + for(start = ptr; *ptr && *ptr != ' '; ptr++) + ; + os = curlx_memdup0(start, ptr - start); + if(!os) + return CURLE_OUT_OF_MEMORY; - /* run the state-machine */ - result = ftp_statemach(data, ftpc, dophase_done); + /* Check for special servers here. */ + if(curl_strequal(os, "OS/400")) { + /* Force OS400 name format 1. */ + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1"); + if(result) { + curlx_free(os); + return result; + } + /* remember target server OS */ + curlx_free(ftpc->server_os); + ftpc->server_os = os; + ftp_state(data, ftpc, FTP_NAMEFMT); + break; + } + /* Nothing special for the target server. */ + /* remember target server OS */ + curlx_free(ftpc->server_os); + ftpc->server_os = os; + } + else { + /* Cannot identify server OS. Continue anyway and cross fingers. */ + } - *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET); + ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */ + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc)); + break; - if(*connected) - infof(data, "[FTP] [%s] perform, DATA connection established", - FTP_CSTATE(ftpc)); - else - CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect", - FTP_CSTATE(ftpc)); + case FTP_NAMEFMT: + if(ftpcode == 250) { + /* Name format change successful: reload initial path. */ + ftp_state_pwd(data, ftpc); + break; + } - if(*dophase_done) - CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_CSTATE(ftpc)); + ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */ + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc)); + break; - return result; -} + case FTP_QUOTE: + case FTP_POSTQUOTE: + case FTP_RETR_PREQUOTE: + case FTP_STOR_PREQUOTE: + case FTP_LIST_PREQUOTE: + if((ftpcode >= 400) && !ftpc->count2) { + /* failure response code, and not allowed to fail */ + failf(data, "QUOT command failed with %03d", ftpcode); + result = CURLE_QUOTE_ERROR; + } + else + result = ftp_state_quote(data, ftpc, ftp, FALSE, ftpc->state); + break; -static void wc_data_dtor(void *ptr) -{ - struct ftp_wc *ftpwc = ptr; - if(ftpwc && ftpwc->parser) - Curl_ftp_parselist_data_free(&ftpwc->parser); - curlx_free(ftpwc); -} + case FTP_CWD: + if(ftpcode / 100 != 2) { + /* failure to CWD there */ + if(data->set.ftp_create_missing_dirs && + ftpc->cwdcount && !ftpc->count2) { + /* try making it */ + ftpc->count2++; /* counter to prevent CWD-MKD loops */ -static CURLcode init_wc_data(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) -{ - char *last_slash; - char *path = ftp->path; - struct WildcardData *wildcard = data->wildcard; - CURLcode result = CURLE_OK; - struct ftp_wc *ftpwc = NULL; + /* count3 is set to allow MKD to fail once per dir. In the case when + CWD fails and then MKD fails (due to another session raced it to + create the dir) this then allows for a second try to CWD to it. */ + ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0; - last_slash = strrchr(ftp->path, '/'); - if(last_slash) { - last_slash++; - if(last_slash[0] == '\0') { - wildcard->state = CURLWC_CLEAN; - return ftp_parse_url_path(data, ftpc, ftp); + result = Curl_pp_sendf(data, &ftpc->pp, "MKD %.*s", + pathlen(ftpc, ftpc->cwdcount - 1), + pathpiece(ftpc, ftpc->cwdcount - 1)); + if(!result) + ftp_state(data, ftpc, FTP_MKD); + } + else { + /* return failure */ + failf(data, "Server denied you to change to the given directory"); + ftpc->cwdfail = TRUE; /* do not remember this path as we failed + to enter it */ + result = CURLE_REMOTE_ACCESS_DENIED; + } } - wildcard->pattern = curlx_strdup(last_slash); - if(!wildcard->pattern) - return CURLE_OUT_OF_MEMORY; - last_slash[0] = '\0'; /* cut file from path */ - } - else { /* there is only 'wildcard pattern' or nothing */ - if(path[0]) { - wildcard->pattern = curlx_strdup(path); - if(!wildcard->pattern) - return CURLE_OUT_OF_MEMORY; - path[0] = '\0'; + else { + /* success */ + ftpc->count2 = 0; + if(ftpc->cwdcount >= ftpc->dirdepth) + result = ftp_state_mdtm(data, ftpc, ftp); + else { + ftpc->cwdcount++; + /* send next CWD */ + result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s", + pathlen(ftpc, ftpc->cwdcount - 1), + pathpiece(ftpc, ftpc->cwdcount - 1)); + } } - else { /* only list */ - wildcard->state = CURLWC_CLEAN; - return ftp_parse_url_path(data, ftpc, ftp); + break; + + case FTP_MKD: + if((ftpcode / 100 != 2) && !ftpc->count3--) { + /* failure to MKD the directory */ + failf(data, "Failed to MKD dir: %03d", ftpcode); + result = CURLE_REMOTE_ACCESS_DENIED; } - } + else { + ftp_state(data, ftpc, FTP_CWD); + /* send CWD */ + result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s", + pathlen(ftpc, ftpc->cwdcount - 1), + pathpiece(ftpc, ftpc->cwdcount - 1)); + } + break; - /* program continues only if URL is not ending with slash, allocate needed - resources for wildcard transfer */ + case FTP_MDTM: + result = ftp_state_mdtm_resp(data, ftpc, ftp, ftpcode); + break; - /* allocate ftp protocol specific wildcard data */ - ftpwc = curlx_calloc(1, sizeof(struct ftp_wc)); - if(!ftpwc) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } + case FTP_TYPE: + case FTP_LIST_TYPE: + case FTP_RETR_TYPE: + case FTP_STOR_TYPE: + case FTP_RETR_LIST_TYPE: + result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state); + break; - /* INITIALIZE parselist structure */ - ftpwc->parser = Curl_ftp_parselist_data_alloc(); - if(!ftpwc->parser) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } + case FTP_SIZE: + case FTP_RETR_SIZE: + case FTP_STOR_SIZE: + result = ftp_state_size_resp(data, ftpc, ftp, ftpcode, ftpc->state); + break; - wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */ - wildcard->dtor = wc_data_dtor; + case FTP_REST: + case FTP_RETR_REST: + result = ftp_state_rest_resp(data, ftpc, ftp, ftpcode, ftpc->state); + break; - /* wildcard does not support NOCWD option (assert it?) */ - if(data->set.ftp_filemethod == FTPFILE_NOCWD) - data->set.ftp_filemethod = FTPFILE_MULTICWD; + case FTP_PRET: + if(ftpcode != 200) { + /* there only is this one standard OK return code. */ + failf(data, "PRET command not accepted: %03d", ftpcode); + return CURLE_FTP_PRET_FAILED; + } + result = ftp_state_use_pasv(data, ftpc, conn); + break; - /* try to parse ftp URL */ - result = ftp_parse_url_path(data, ftpc, ftp); - if(result) { - goto fail; - } + case FTP_PASV: + result = ftp_state_pasv_resp(data, ftpc, ftpcode); + break; - wildcard->path = curlx_strdup(ftp->path); - if(!wildcard->path) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } + case FTP_PORT: + result = ftp_state_port_resp(data, ftpc, ftp, ftpcode); + break; - /* backup old write_function */ - ftpwc->backup.write_function = data->set.fwrite_func; - /* parsing write function */ - data->set.fwrite_func = Curl_ftp_parselist; - /* backup old file descriptor */ - ftpwc->backup.file_descriptor = data->set.out; - /* let the writefunc callback know the transfer */ - data->set.out = data; + case FTP_LIST: + case FTP_RETR: + result = ftp_state_get_resp(data, ftpc, ftp, ftpcode, ftpc->state); + break; - infof(data, "Wildcard - Parsing started"); - return CURLE_OK; + case FTP_STOR: + result = ftp_state_stor_resp(data, ftpc, ftpcode); + break; -fail: - if(ftpwc) { - Curl_ftp_parselist_data_free(&ftpwc->parser); - curlx_free(ftpwc); + case FTP_QUIT: + default: + /* internal error */ + ftp_state(data, ftpc, FTP_STOP); + break; } - Curl_safefree(wildcard->pattern); - wildcard->dtor = ZERO_NULL; - wildcard->ftpwc = NULL; + return result; } -static CURLcode wc_statemach(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) +/* called repeatedly until done from multi.c */ +static CURLcode ftp_multi_statemach(struct Curl_easy *data, + bool *done) { - struct WildcardData * const wildcard = data->wildcard; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + return ftpc ? ftp_statemach(data, ftpc, done) : CURLE_FAILED_INIT; +} + +static CURLcode ftp_block_statemach(struct Curl_easy *data, + struct ftp_conn *ftpc) +{ + struct pingpong *pp = &ftpc->pp; CURLcode result = CURLE_OK; - for(;;) { - switch(wildcard->state) { - case CURLWC_INIT: - result = init_wc_data(data, ftpc, ftp); - if(wildcard->state == CURLWC_CLEAN) - /* only listing! */ - return result; - wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING; - return result; + while(ftpc->state != FTP_STOP) { + if(ftpc->shutdown) + CURL_TRC_FTP(data, "in shutdown, waiting for server response"); + result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */); + if(result) + break; + } - 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->ftpwc; - data->set.fwrite_func = ftpwc->backup.write_function; - data->set.out = ftpwc->backup.file_descriptor; - ftpwc->backup.write_function = ZERO_NULL; - ftpwc->backup.file_descriptor = NULL; - wildcard->state = CURLWC_DOWNLOADING; + return result; +} - if(Curl_ftp_parselist_geterror(ftpwc->parser)) { - /* error found in LIST parsing */ - wildcard->state = CURLWC_CLEAN; - continue; - } - if(Curl_llist_count(&wildcard->filelist) == 0) { - /* no corresponding file */ - wildcard->state = CURLWC_CLEAN; - return CURLE_REMOTE_FILE_NOT_FOUND; - } - continue; - } +/* + * ftp_connect() should do everything that is to be considered a part of + * the connection phase. + * + * The variable 'done' points to will be TRUE if the protocol-layer connect + * phase is done when this function returns, or FALSE if not. + * + */ +static CURLcode ftp_connect(struct Curl_easy *data, + bool *done) /* see description above */ +{ + CURLcode result; + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + struct pingpong *pp; - case CURLWC_DOWNLOADING: { - /* filelist has at least one file, lets get first one */ - struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist); - struct curl_fileinfo *finfo = Curl_node_elem(head); + *done = FALSE; /* default to not done yet */ + if(!ftpc) + return CURLE_FAILED_INIT; + pp = &ftpc->pp; + PINGPONG_SETUP(pp, ftp_pp_statemachine, ftp_endofresp); - char *tmp_path = curl_maprintf("%s%s", wildcard->path, finfo->filename); - if(!tmp_path) - return CURLE_OUT_OF_MEMORY; + if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + /* BLOCKING */ + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); + if(result) + return result; + conn->bits.ftp_use_control_ssl = TRUE; + } - /* switch default ftp->path and tmp_path */ - curlx_free(ftp->pathalloc); - ftp->pathalloc = ftp->path = tmp_path; + Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */ - infof(data, "Wildcard - START of \"%s\"", finfo->filename); - if(data->set.chunk_bgn) { - long userresponse; - Curl_set_in_callback(data, TRUE); - userresponse = data->set.chunk_bgn( - finfo, data->set.wildcardptr, - (int)Curl_llist_count(&wildcard->filelist)); - Curl_set_in_callback(data, FALSE); - switch(userresponse) { - case CURL_CHUNK_BGN_FUNC_SKIP: - infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename); - wildcard->state = CURLWC_SKIP; - continue; - case CURL_CHUNK_BGN_FUNC_FAIL: - return CURLE_CHUNK_FAILED; - } - } + /* When we connect, we start in the state where we await the 220 + response */ + ftp_state(data, ftpc, FTP_WAIT220); - if(finfo->filetype != CURLFILETYPE_FILE) { - wildcard->state = CURLWC_SKIP; - continue; - } + result = ftp_statemach(data, ftpc, done); - if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE) - ftpc->known_filesize = finfo->size; + return result; +} - result = ftp_parse_url_path(data, ftpc, ftp); - if(result) - return result; +/*********************************************************************** + * + * ftp_sendquote() + * + * Where a 'quote' means a list of custom commands to send to the server. + * The quote list is passed as an argument. + * + * BLOCKING + */ +static CURLcode ftp_sendquote(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct curl_slist *quote) +{ + struct curl_slist *item; + struct pingpong *pp = &ftpc->pp; - /* we do not need the Curl_fileinfo of first file anymore */ - Curl_node_remove(Curl_llist_head(&wildcard->filelist)); + item = quote; + while(item) { + if(item->data) { + size_t nread; + const char *cmd = item->data; + bool acceptfail = FALSE; + CURLcode result; + int ftpcode = 0; - if(Curl_llist_count(&wildcard->filelist) == 0) { - /* remains only one file to down. */ - wildcard->state = CURLWC_CLEAN; - /* after that will be ftp_do called once again and no transfer - will be done because of CURLWC_CLEAN state */ - return CURLE_OK; - } - return result; - } + /* if a command starts with an asterisk, which a legal FTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ - case CURLWC_SKIP: { - if(data->set.chunk_end) { - Curl_set_in_callback(data, TRUE); - data->set.chunk_end(data->set.wildcardptr); - Curl_set_in_callback(data, FALSE); + if(cmd[0] == '*') { + cmd++; + acceptfail = TRUE; } - Curl_node_remove(Curl_llist_head(&wildcard->filelist)); - wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ? - CURLWC_CLEAN : CURLWC_DOWNLOADING; - continue; - } - - case CURLWC_CLEAN: { - struct ftp_wc *ftpwc = wildcard->ftpwc; - result = CURLE_OK; - if(ftpwc) - result = Curl_ftp_parselist_geterror(ftpwc->parser); - wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE; - return result; - } + result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); + if(!result) { + pp->response = *Curl_pgrs_now(data); /* timeout relative now */ + result = getftpresponse(data, &nread, &ftpcode); + } + if(result) + return result; - case CURLWC_DONE: - case CURLWC_ERROR: - case CURLWC_CLEAR: - if(wildcard->dtor) { - wildcard->dtor(wildcard->ftpwc); - wildcard->ftpwc = NULL; + if(!acceptfail && (ftpcode >= 400)) { + failf(data, "QUOT string not accepted: %s", cmd); + return CURLE_QUOTE_ERROR; } - return result; } + + item = item->next; } - /* UNREACHABLE */ + + return CURLE_OK; } /*********************************************************************** * - * ftp_do() + * ftp_done() * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (ftp_perform). + * The DONE function. This does what needs to be done after a single DO has + * performed. * - * The input argument is already checked for validity. + * Input argument is already checked for validity. */ -static CURLcode ftp_do(struct Curl_easy *data, bool *done) +static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, + bool premature) { - CURLcode result = CURLE_OK; - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + struct connectdata *conn = data->conn; struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + struct pingpong *pp; + size_t nread; + int ftpcode; + CURLcode result = CURLE_OK; + + if(!ftp || !ftpc) + return CURLE_OK; + + pp = &ftpc->pp; + switch(status) { + case CURLE_BAD_DOWNLOAD_RESUME: + case CURLE_FTP_WEIRD_PASV_REPLY: + case CURLE_FTP_PORT_FAILED: + case CURLE_FTP_ACCEPT_FAILED: + case CURLE_FTP_ACCEPT_TIMEOUT: + case CURLE_FTP_COULDNT_SET_TYPE: + case CURLE_FTP_COULDNT_RETR_FILE: + case CURLE_PARTIAL_FILE: + case CURLE_UPLOAD_FAILED: + case CURLE_REMOTE_ACCESS_DENIED: + case CURLE_FILESIZE_EXCEEDED: + case CURLE_REMOTE_FILE_NOT_FOUND: + case CURLE_WRITE_ERROR: + /* the connection stays alive fine even though this happened */ + case CURLE_OK: /* does not affect the control connection's status */ + if(!premature) + break; + + /* until we cope better with prematurely ended requests, let them + * fallback as if in complete failure */ + FALLTHROUGH(); + default: /* by default, an error means the control connection is + wedged and should not be used anymore */ + ftpc->ctl_valid = FALSE; + ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the + current path, as this connection is going */ + connclose(conn, "FTP ended with bad error code"); + result = status; /* use the already set error code */ + break; + } + + if(data->state.wildcardmatch) { + if(data->set.chunk_end && ftpc->file) { + Curl_set_in_callback(data, TRUE); + data->set.chunk_end(data->set.wildcardptr); + Curl_set_in_callback(data, FALSE); + freedirs(ftpc); + } + ftpc->known_filesize = -1; + } + + if(result) { + /* We can limp along anyway (and should try to since we may already be in + * the error path) */ + ftpc->ctl_valid = FALSE; /* mark control connection as bad */ + connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ + curlx_free(ftpc->prevpath); + ftpc->prevpath = NULL; /* no path remembering */ + } + else { /* remember working directory for connection reuse */ + const char *rawPath = ftpc->rawpath; + if(rawPath) { + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + ; /* full path => no CWDs happened => keep ftpc->prevpath */ + else { + size_t pathLen = strlen(ftpc->rawpath); + + curlx_free(ftpc->prevpath); + + if(!ftpc->cwdfail) { + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + pathLen = 0; /* relative path => working directory is FTP home */ + else + /* file is url-decoded */ + pathLen -= ftpc->file ? strlen(ftpc->file) : 0; + ftpc->prevpath = curlx_memdup0(rawPath, pathLen); + } + else + ftpc->prevpath = NULL; /* no path */ + } + } + if(ftpc->prevpath) + infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath); + } + + /* shut down the socket to inform the server we are done */ + + if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) { + if(!result && ftpc->dont_check && data->req.maxdownload > 0) { + /* partial download completed */ + result = Curl_pp_sendf(data, pp, "%s", "ABOR"); + if(result) { + failf(data, "Failure sending ABOR command: %s", + curl_easy_strerror(result)); + ftpc->ctl_valid = FALSE; /* mark control connection as bad */ + connclose(conn, "ABOR command failed"); /* connection closure */ + } + } - *done = FALSE; /* default to false */ - if(!ftpc || !ftp) - return CURLE_FAILED_INIT; - ftpc->wait_data_conn = FALSE; /* default to no such wait */ + close_secondarysocket(data, ftpc); + } -#ifdef CURL_PREFER_LF_LINEENDS - { - /* FTP data may need conversion. */ - struct Curl_cwriter *ftp_lc_writer; + if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && + pp->pending_resp && !premature) { + /* + * Let's see what the server says about the transfer we performed, but + * lower the timeout as sometimes this connection has died while the data + * has been transferred. This happens when doing through NATs etc that + * abandon old silent connections. + */ + pp->response = *Curl_pgrs_now(data); /* timeout relative now */ + result = getftpresponse(data, &nread, &ftpcode); + + if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) { + failf(data, "control connection looks dead"); + ftpc->ctl_valid = FALSE; /* mark control connection as bad */ + connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */ + } - result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc, - CURL_CW_CONTENT_DECODE); if(result) return result; - result = Curl_cwriter_add(data, ftp_lc_writer); - if(result) { - Curl_cwriter_free(data, ftp_lc_writer); + if(ftpc->dont_check && data->req.maxdownload > 0) { + /* we have sent ABOR and there is no reliable way to check if it was + * successful or not; we have to close the connection now */ + infof(data, "partial download completed, closing connection"); + connclose(conn, "Partial download with no ability to check"); return result; } + + if(!ftpc->dont_check) { + /* 226 Transfer complete, 250 Requested file action okay, completed. */ + switch(ftpcode) { + case 226: + case 250: + break; + case 552: + failf(data, "Exceeded storage allocation"); + result = CURLE_REMOTE_DISK_FULL; + break; + default: + failf(data, "server did not report OK, got %d", ftpcode); + result = CURLE_PARTIAL_FILE; + break; + } + } } -#endif /* CURL_PREFER_LF_LINEENDS */ - if(data->state.wildcardmatch) { - result = wc_statemach(data, ftpc, ftp); - if(data->wildcard->state == CURLWC_SKIP || - data->wildcard->state == CURLWC_DONE) { - /* do not call ftp_regular_transfer */ - return CURLE_OK; + if(result || premature) + /* the response code from the transfer showed an error already so no + use checking further */ + ; + else if(data->state.upload) { + if((ftp->transfer == PPTRANSFER_BODY) && + (data->state.infilesize != -1) && /* upload with known size */ + ((!data->set.crlf && !data->state.prefer_ascii && /* no conversion */ + (data->state.infilesize != data->req.writebytecount)) || + ((data->set.crlf || data->state.prefer_ascii) && /* maybe crlf conv */ + (data->state.infilesize > data->req.writebytecount)) + )) { + failf(data, "Uploaded unaligned file size (%" FMT_OFF_T + " out of %" FMT_OFF_T " bytes)", + data->req.writebytecount, data->state.infilesize); + result = CURLE_PARTIAL_FILE; } - if(result) /* error, loop or skipping the file */ - return result; } - else { /* no wildcard FSM needed */ - result = ftp_parse_url_path(data, ftpc, ftp); - if(result) - return result; + else { + if((data->req.size != -1) && + (data->req.size != data->req.bytecount) && + (data->req.maxdownload != data->req.bytecount)) { + failf(data, "Received only partial file: %" FMT_OFF_T " bytes", + data->req.bytecount); + result = CURLE_PARTIAL_FILE; + } + else if(!ftpc->dont_check && + !data->req.bytecount && + (data->req.size > 0)) { + failf(data, "No data was received"); + result = CURLE_FTP_COULDNT_RETR_FILE; + } } - result = ftp_regular_transfer(data, ftpc, ftp, done); + /* clear these for next connection */ + ftp->transfer = PPTRANSFER_BODY; + ftpc->dont_check = FALSE; + /* Send any post-transfer QUOTE strings? */ + if(!status && !result && !premature && data->set.postquote) + result = ftp_sendquote(data, ftpc, data->set.postquote); + CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), result); return result; } /*********************************************************************** * - * ftp_quit() - * - * This should be called before calling sclose() on an ftp control connection - * (not data connections). We should then wait for the response from the - * server before returning. The calling code should then try to close the - * connection. + * ftp_nb_type() * + * Set TYPE. We only deal with ASCII or BINARY so this function + * sets one of them. + * If the transfer type is not sent, simulate on OK response in newstate */ -static CURLcode ftp_quit(struct Curl_easy *data, - struct ftp_conn *ftpc) +static CURLcode ftp_nb_type(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool ascii, ftpstate newstate) { - CURLcode result = CURLE_OK; + CURLcode result; + char want = (char)(ascii ? 'A' : 'I'); - if(ftpc->ctl_valid) { - CURL_TRC_FTP(data, "sending QUIT to close session"); - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "QUIT"); - if(result) { - failf(data, "Failure sending QUIT command: %s", - curl_easy_strerror(result)); - ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(data->conn, "QUIT command failed"); /* mark for closure */ - ftp_state(data, ftpc, FTP_STOP); - return result; - } + if(ftpc->transfertype == want) { + ftp_state(data, ftpc, newstate); + return ftp_state_type_resp(data, ftpc, ftp, 200, newstate); + } - ftp_state(data, ftpc, FTP_QUIT); + result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want); + if(!result) { + ftp_state(data, ftpc, newstate); - result = ftp_block_statemach(data, ftpc); + /* keep track of our current transfer type */ + ftpc->transfertype = want; } - return result; } /*********************************************************************** * - * ftp_disconnect() - * - * Disconnect from an FTP server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode ftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - - if(!ftpc) - return CURLE_FAILED_INIT; - /* We cannot send quit unconditionally. If this connection is stale or - bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to. - - ftp_quit() will check the state of ftp->ctl_valid. If it is ok it - will try to send the QUIT command, otherwise it will just return. - */ - ftpc->shutdown = TRUE; - if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp)) - ftpc->ctl_valid = FALSE; - - /* The FTP session may or may not have been allocated/setup at this point! */ - (void)ftp_quit(data, ftpc); /* ignore errors on the QUIT */ - return CURLE_OK; -} - -static size_t numof_slashes(const char *str) -{ - const char *slashPos; - size_t num = 0; - do { - slashPos = strchr(str, '/'); - if(slashPos) { - ++num; - str = slashPos + 1; - } - } while(slashPos); - return num; -} - -#define FTP_MAX_DIR_DEPTH 1000 - -/*********************************************************************** - * - * ftp_parse_url_path() - * - * Parse the URL path into separate path components. + * ftp_perform() * + * This is the actual DO function for FTP. Get a file/directory according to + * the options previously setup. */ -static CURLcode ftp_parse_url_path(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) +static +CURLcode ftp_perform(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool *connected, /* connect status after PASV / PORT */ + bool *dophase_done) { - const char *slashPos = NULL; - const char *fileName = NULL; + /* this is FTP and no proxy */ CURLcode result = CURLE_OK; - const char *rawPath = NULL; /* url-decoded "raw" path */ - size_t pathLen = 0; - ftpc->ctl_valid = FALSE; - ftpc->cwdfail = FALSE; + CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_CSTATE(ftpc)); - if(ftpc->rawpath) - freedirs(ftpc); - /* url-decode ftp path before further evaluation */ - result = Curl_urldecode(ftp->path, 0, &ftpc->rawpath, &pathLen, REJECT_CTRL); - if(result) { - failf(data, "path contains control characters"); - return result; + if(data->req.no_body) { + /* requested no body means no transfer... */ + ftp->transfer = PPTRANSFER_INFO; } - rawPath = ftpc->rawpath; - - switch(data->set.ftp_filemethod) { - case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ - - if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) - fileName = rawPath; /* this is a full file path */ - /* - else: ftpc->file is not used anywhere other than for operations on - a file. In other words, never for directory operations. - So we can safely leave filename as NULL here and use it as a - argument in dir/file decisions. - */ - break; - - case FTPFILE_SINGLECWD: - slashPos = strrchr(rawPath, '/'); - if(slashPos) { - /* get path before last slash, except for / */ - size_t dirlen = slashPos - rawPath; - if(dirlen == 0) - dirlen = 1; - - ftpc->dirs = curlx_calloc(1, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; - ftpc->dirs[0].start = 0; - ftpc->dirs[0].len = (int)dirlen; - ftpc->dirdepth = 1; /* we consider it to be a single directory */ - fileName = slashPos + 1; /* rest is filename */ - } - else - fileName = rawPath; /* filename only (or empty) */ - break; + *dophase_done = FALSE; /* not done yet */ - default: /* allow pretty much anything */ - case FTPFILE_MULTICWD: { - /* current position: begin of next path component */ - const char *curPos = rawPath; + /* start the first command in the DO phase */ + result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_QUOTE); + if(result) + return result; - /* number of entries to allocate for the 'dirs' array */ - size_t dirAlloc = numof_slashes(rawPath); + /* run the state-machine */ + result = ftp_statemach(data, ftpc, dophase_done); - if(dirAlloc >= FTP_MAX_DIR_DEPTH) - /* suspiciously deep directory hierarchy */ - return CURLE_URL_MALFORMAT; + *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET); - if(dirAlloc) { - ftpc->dirs = curlx_calloc(dirAlloc, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; + if(*connected) + infof(data, "[FTP] [%s] perform, DATA connection established", + FTP_CSTATE(ftpc)); + else + CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect", + FTP_CSTATE(ftpc)); - /* parse the URL path into separate path components */ - while(dirAlloc--) { - const char *spos = strchr(curPos, '/'); - size_t clen = spos - curPos; + if(*dophase_done) + CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_CSTATE(ftpc)); - /* path starts with a slash: add that as a directory */ - if(!clen && (ftpc->dirdepth == 0)) - ++clen; + return result; +} - /* we skip empty path components, like "x//y" since the FTP command - CWD requires a parameter and a non-existent parameter a) does not - work on many servers and b) has no effect on the others. */ - if(clen) { - ftpc->dirs[ftpc->dirdepth].start = (int)(curPos - rawPath); - ftpc->dirs[ftpc->dirdepth].len = (int)clen; - ftpc->dirdepth++; - } - curPos = spos + 1; - } +static void wc_data_dtor(void *ptr) +{ + struct ftp_wc *ftpwc = ptr; + if(ftpwc && ftpwc->parser) + Curl_ftp_parselist_data_free(&ftpwc->parser); + curlx_free(ftpwc); +} + +static CURLcode init_wc_data(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + char *last_slash; + char *path = ftp->path; + struct WildcardData *wildcard = data->wildcard; + CURLcode result = CURLE_OK; + struct ftp_wc *ftpwc = NULL; + + last_slash = strrchr(ftp->path, '/'); + if(last_slash) { + last_slash++; + if(last_slash[0] == '\0') { + wildcard->state = CURLWC_CLEAN; + return ftp_parse_url_path(data, ftpc, ftp); + } + wildcard->pattern = curlx_strdup(last_slash); + if(!wildcard->pattern) + return CURLE_OUT_OF_MEMORY; + last_slash[0] = '\0'; /* cut file from path */ + } + else { /* there is only 'wildcard pattern' or nothing */ + if(path[0]) { + wildcard->pattern = curlx_strdup(path); + if(!wildcard->pattern) + return CURLE_OUT_OF_MEMORY; + path[0] = '\0'; + } + else { /* only list */ + wildcard->state = CURLWC_CLEAN; + return ftp_parse_url_path(data, ftpc, ftp); } - fileName = curPos; /* the rest is the filename (or empty) */ } - break; - } /* switch */ - if(fileName && *fileName) - ftpc->file = fileName; - else - ftpc->file = NULL; /* instead of point to a zero byte, - we make it a NULL pointer */ + /* program continues only if URL is not ending with slash, allocate needed + resources for wildcard transfer */ - if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { - /* We need a filename when uploading. Return error! */ - failf(data, "Uploading to a URL without a filename"); - return CURLE_URL_MALFORMAT; + /* allocate ftp protocol specific wildcard data */ + ftpwc = curlx_calloc(1, sizeof(struct ftp_wc)); + if(!ftpwc) { + result = CURLE_OUT_OF_MEMORY; + goto fail; } - ftpc->cwddone = FALSE; /* default to not done */ + /* INITIALIZE parselist structure */ + ftpwc->parser = Curl_ftp_parselist_data_alloc(); + if(!ftpwc->parser) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) - ftpc->cwddone = TRUE; /* skip CWD for absolute paths */ - else { /* newly created FTP connections are already in entry path */ - const char *oldPath = data->conn->bits.reuse ? ftpc->prevpath : ""; - if(oldPath) { - size_t n = pathLen; - if(data->set.ftp_filemethod == FTPFILE_NOCWD) - n = 0; /* CWD to entry for relative paths */ - else - n -= ftpc->file ? strlen(ftpc->file) : 0; + wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */ + wildcard->dtor = wc_data_dtor; - if((strlen(oldPath) == n) && rawPath && !strncmp(rawPath, oldPath, n)) { - infof(data, "Request has same path as previous transfer"); - ftpc->cwddone = TRUE; - } - } + /* wildcard does not support NOCWD option (assert it?) */ + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + data->set.ftp_filemethod = FTPFILE_MULTICWD; + + /* try to parse ftp URL */ + result = ftp_parse_url_path(data, ftpc, ftp); + if(result) { + goto fail; + } + + wildcard->path = curlx_strdup(ftp->path); + if(!wildcard->path) { + result = CURLE_OUT_OF_MEMORY; + goto fail; } + /* backup old write_function */ + ftpwc->backup.write_function = data->set.fwrite_func; + /* parsing write function */ + data->set.fwrite_func = Curl_ftp_parselist; + /* backup old file descriptor */ + ftpwc->backup.file_descriptor = data->set.out; + /* let the writefunc callback know the transfer */ + data->set.out = data; + + infof(data, "Wildcard - Parsing started"); return CURLE_OK; + +fail: + if(ftpwc) { + Curl_ftp_parselist_data_free(&ftpwc->parser); + curlx_free(ftpwc); + } + Curl_safefree(wildcard->pattern); + wildcard->dtor = ZERO_NULL; + wildcard->ftpwc = NULL; + return result; } -/* call this when the DO phase has completed */ -static CURLcode ftp_dophase_done(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool connected) +static CURLcode wc_statemach(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) { - if(connected) { - int completed; - CURLcode result = ftp_do_more(data, &completed); + struct WildcardData * const wildcard = data->wildcard; + CURLcode result = CURLE_OK; - if(result) { - close_secondarysocket(data, ftpc); + for(;;) { + switch(wildcard->state) { + case CURLWC_INIT: + result = init_wc_data(data, ftpc, ftp); + if(wildcard->state == CURLWC_CLEAN) + /* only listing! */ + return result; + wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING; return result; + + case CURLWC_MATCHING: { + /* In this state is LIST response successfully parsed, so lets restore + previous WRITEFUNCTION callback and WRITEDATA pointer */ + struct ftp_wc *ftpwc = wildcard->ftpwc; + data->set.fwrite_func = ftpwc->backup.write_function; + data->set.out = ftpwc->backup.file_descriptor; + ftpwc->backup.write_function = ZERO_NULL; + ftpwc->backup.file_descriptor = NULL; + wildcard->state = CURLWC_DOWNLOADING; + + if(Curl_ftp_parselist_geterror(ftpwc->parser)) { + /* error found in LIST parsing */ + wildcard->state = CURLWC_CLEAN; + continue; + } + if(Curl_llist_count(&wildcard->filelist) == 0) { + /* no corresponding file */ + wildcard->state = CURLWC_CLEAN; + return CURLE_REMOTE_FILE_NOT_FOUND; + } + continue; } - } - if(ftp->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_xfer_setup_nop(data); - else if(!connected) - /* since we did not connect now, we want do_more to get called */ - data->conn->bits.do_more = TRUE; + case CURLWC_DOWNLOADING: { + /* filelist has at least one file, lets get first one */ + struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist); + struct curl_fileinfo *finfo = Curl_node_elem(head); + + char *tmp_path = curl_maprintf("%s%s", wildcard->path, finfo->filename); + if(!tmp_path) + return CURLE_OUT_OF_MEMORY; + + /* switch default ftp->path and tmp_path */ + curlx_free(ftp->pathalloc); + ftp->pathalloc = ftp->path = tmp_path; + + infof(data, "Wildcard - START of \"%s\"", finfo->filename); + if(data->set.chunk_bgn) { + long userresponse; + Curl_set_in_callback(data, TRUE); + userresponse = data->set.chunk_bgn( + finfo, data->set.wildcardptr, + (int)Curl_llist_count(&wildcard->filelist)); + Curl_set_in_callback(data, FALSE); + switch(userresponse) { + case CURL_CHUNK_BGN_FUNC_SKIP: + infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename); + wildcard->state = CURLWC_SKIP; + continue; + case CURL_CHUNK_BGN_FUNC_FAIL: + return CURLE_CHUNK_FAILED; + } + } + + if(finfo->filetype != CURLFILETYPE_FILE) { + wildcard->state = CURLWC_SKIP; + continue; + } + + if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE) + ftpc->known_filesize = finfo->size; + + result = ftp_parse_url_path(data, ftpc, ftp); + if(result) + return result; - ftpc->ctl_valid = TRUE; /* seems good */ + /* we do not need the Curl_fileinfo of first file anymore */ + Curl_node_remove(Curl_llist_head(&wildcard->filelist)); - return CURLE_OK; -} + if(Curl_llist_count(&wildcard->filelist) == 0) { + /* remains only one file to down. */ + wildcard->state = CURLWC_CLEAN; + /* after that will be ftp_do called once again and no transfer + will be done because of CURLWC_CLEAN state */ + return CURLE_OK; + } + return result; + } -/* called from multi.c while DOing */ -static CURLcode ftp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); - CURLcode result; + case CURLWC_SKIP: { + if(data->set.chunk_end) { + Curl_set_in_callback(data, TRUE); + data->set.chunk_end(data->set.wildcardptr); + Curl_set_in_callback(data, FALSE); + } + Curl_node_remove(Curl_llist_head(&wildcard->filelist)); + wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ? + CURLWC_CLEAN : CURLWC_DOWNLOADING; + continue; + } - if(!ftpc || !ftp) - return CURLE_FAILED_INIT; - result = ftp_statemach(data, ftpc, dophase_done); + case CURLWC_CLEAN: { + struct ftp_wc *ftpwc = wildcard->ftpwc; + result = CURLE_OK; + if(ftpwc) + result = Curl_ftp_parselist_geterror(ftpwc->parser); - if(result) - CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_CSTATE(ftpc)); - else if(*dophase_done) { - result = ftp_dophase_done(data, ftpc, ftp, FALSE /* not connected */); + wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE; + return result; + } - CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_CSTATE(ftpc)); + case CURLWC_DONE: + case CURLWC_ERROR: + case CURLWC_CLEAR: + if(wildcard->dtor) { + wildcard->dtor(wildcard->ftpwc); + wildcard->ftpwc = NULL; + } + return result; + } } - return result; + /* UNREACHABLE */ } /*********************************************************************** @@ -4346,6 +4188,153 @@ static CURLcode ftp_regular_transfer(struct Curl_easy *data, return result; } +/*********************************************************************** + * + * ftp_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (ftp_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode ftp_do(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); + + *done = FALSE; /* default to false */ + if(!ftpc || !ftp) + return CURLE_FAILED_INIT; + ftpc->wait_data_conn = FALSE; /* default to no such wait */ + +#ifdef CURL_PREFER_LF_LINEENDS + { + /* FTP data may need conversion. */ + struct Curl_cwriter *ftp_lc_writer; + + result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc, + CURL_CW_CONTENT_DECODE); + if(result) + return result; + + result = Curl_cwriter_add(data, ftp_lc_writer); + if(result) { + Curl_cwriter_free(data, ftp_lc_writer); + return result; + } + } +#endif /* CURL_PREFER_LF_LINEENDS */ + + if(data->state.wildcardmatch) { + result = wc_statemach(data, ftpc, ftp); + if(data->wildcard->state == CURLWC_SKIP || + data->wildcard->state == CURLWC_DONE) { + /* do not call ftp_regular_transfer */ + return CURLE_OK; + } + if(result) /* error, loop or skipping the file */ + return result; + } + else { /* no wildcard FSM needed */ + result = ftp_parse_url_path(data, ftpc, ftp); + if(result) + return result; + } + + result = ftp_regular_transfer(data, ftpc, ftp, done); + + return result; +} + +/*********************************************************************** + * + * ftp_quit() + * + * This should be called before calling sclose() on an ftp control connection + * (not data connections). We should then wait for the response from the + * server before returning. The calling code should then try to close the + * connection. + * + */ +static CURLcode ftp_quit(struct Curl_easy *data, + struct ftp_conn *ftpc) +{ + CURLcode result = CURLE_OK; + + if(ftpc->ctl_valid) { + CURL_TRC_FTP(data, "sending QUIT to close session"); + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "QUIT"); + if(result) { + failf(data, "Failure sending QUIT command: %s", + curl_easy_strerror(result)); + ftpc->ctl_valid = FALSE; /* mark control connection as bad */ + connclose(data->conn, "QUIT command failed"); /* mark for closure */ + ftp_state(data, ftpc, FTP_STOP); + return result; + } + + ftp_state(data, ftpc, FTP_QUIT); + + result = ftp_block_statemach(data, ftpc); + } + + return result; +} + +/*********************************************************************** + * + * ftp_disconnect() + * + * Disconnect from an FTP server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode ftp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); + + if(!ftpc) + return CURLE_FAILED_INIT; + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way, sending quit and waiting around here will make the + disconnect wait in vain and cause more problems than we need to. + + ftp_quit() will check the state of ftp->ctl_valid. If it is ok it + will try to send the QUIT command, otherwise it will return. + */ + ftpc->shutdown = TRUE; + if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp)) + ftpc->ctl_valid = FALSE; + + /* The FTP session may or may not have been allocated/setup at this point! */ + (void)ftp_quit(data, ftpc); /* ignore errors on the QUIT */ + return CURLE_OK; +} + +/* called from multi.c while DOing */ +static CURLcode ftp_doing(struct Curl_easy *data, + bool *dophase_done) +{ + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); + CURLcode result; + + if(!ftpc || !ftp) + return CURLE_FAILED_INIT; + result = ftp_statemach(data, ftpc, dophase_done); + + if(result) + CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_CSTATE(ftpc)); + else if(*dophase_done) { + result = ftp_dophase_done(data, ftpc, ftp, FALSE /* not connected */); + + CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_CSTATE(ftpc)); + } + return result; +} + static void ftp_easy_dtor(void *key, size_t klen, void *entry) { struct FTP *ftp = entry; @@ -4464,4 +4453,64 @@ bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn) return TRUE; } +/* + * FTP protocol. + */ +static const struct Curl_protocol Curl_protocol_ftp = { + ftp_setup_connection, /* setup_connection */ + ftp_do, /* do_it */ + ftp_done, /* done */ + ftp_do_more, /* do_more */ + ftp_connect, /* connect_it */ + ftp_multi_statemach, /* connecting */ + ftp_doing, /* doing */ + ftp_pollset, /* proto_pollset */ + ftp_pollset, /* doing_pollset */ + ftp_domore_pollset, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_FTP */ + +/* + * FTP protocol handler. + */ +const struct Curl_scheme Curl_scheme_ftp = { + "ftp", /* scheme */ +#ifdef CURL_DISABLE_FTP + ZERO_NULL, +#else + &Curl_protocol_ftp, +#endif + CURLPROTO_FTP, /* protocol */ + CURLPROTO_FTP, /* family */ + PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | + PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | + PROTOPT_WILDCARD | PROTOPT_SSL_REUSE | + PROTOPT_CONN_REUSE, /* flags */ + PORT_FTP, /* defport */ +}; + +/* + * FTPS protocol handler. + */ +const struct Curl_scheme Curl_scheme_ftps = { + "ftps", /* scheme */ +#if defined(CURL_DISABLE_FTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_ftp, +#endif + CURLPROTO_FTPS, /* protocol */ + CURLPROTO_FTP, /* family */ + PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | + PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD | + PROTOPT_CONN_REUSE, /* flags */ + PORT_FTPS, /* defport */ +}; diff --git a/vendor/curl/lib/ftp.h b/vendor/curl/lib/ftp.h index 0e32d39..20b3600 100644 --- a/vendor/curl/lib/ftp.h +++ b/vendor/curl/lib/ftp.h @@ -27,13 +27,10 @@ #include "pingpong.h" -#ifndef CURL_DISABLE_FTP -extern const struct Curl_handler Curl_handler_ftp; - -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_ftps; -#endif +extern const struct Curl_scheme Curl_scheme_ftp; +extern const struct Curl_scheme Curl_scheme_ftps; +#ifndef CURL_DISABLE_FTP bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn); #endif /* CURL_DISABLE_FTP */ @@ -110,8 +107,8 @@ struct FTP { char *path; /* points to the urlpieces struct field */ char *pathalloc; /* if non-NULL a pointer to an allocated path */ - /* transfer a file/body or not, done as a typedefed enum just to make - debuggers display the full symbol and not just the numerical value */ + /* transfer a file/body or not, done as a typedefed enum to make debuggers + display the full symbol and not the numerical value */ curl_pp_transfer transfer; curl_off_t downloadsize; }; @@ -154,7 +151,7 @@ struct ftp_conn { 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 - read the line, just ignore the result. */ + read the line, ignore the result. */ BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If the connection has timed out or been closed, this should be FALSE when it gets to Curl_ftp_quit() */ diff --git a/vendor/curl/lib/ftplistparser.h b/vendor/curl/lib/ftplistparser.h index afb90de..5d7aa49 100644 --- a/vendor/curl/lib/ftplistparser.h +++ b/vendor/curl/lib/ftplistparser.h @@ -37,7 +37,7 @@ CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data); struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void); -void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data); +void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp); /* list of wildcard process states */ typedef enum { diff --git a/vendor/curl/lib/functypes.h b/vendor/curl/lib/functypes.h index ba754f9..887c261 100644 --- a/vendor/curl/lib/functypes.h +++ b/vendor/curl/lib/functypes.h @@ -23,8 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "curl_setup.h" - /* defaults: ssize_t recv(int, void *, size_t, int); @@ -37,7 +35,7 @@ 2. For systems with config-*.h files, define them there. */ -#ifdef _WIN32 +#ifdef USE_WINSOCK /* int recv(SOCKET, char *, int, int) */ #define RECV_TYPE_ARG1 SOCKET #define RECV_TYPE_ARG2 char * @@ -59,9 +57,9 @@ #define RECV_TYPE_ARG4 long #define RECV_TYPE_RETV long -/* int send(int, const char *, int, int); */ +/* int send(int, char *, int, int); */ #define SEND_TYPE_ARG1 int -#define SEND_QUAL_ARG2 +#define SEND_NONCONST_ARG2 #define SEND_TYPE_ARG2 char * #define SEND_TYPE_ARG3 int #define SEND_TYPE_RETV int @@ -87,10 +85,6 @@ #define RECV_TYPE_RETV ssize_t #endif -#ifndef SEND_QUAL_ARG2 -#define SEND_QUAL_ARG2 const -#endif - #ifndef SEND_TYPE_ARG1 #define SEND_TYPE_ARG1 int #endif diff --git a/vendor/curl/lib/getinfo.c b/vendor/curl/lib/getinfo.c index e094ffd..dedafd3 100644 --- a/vendor/curl/lib/getinfo.c +++ b/vendor/curl/lib/getinfo.c @@ -127,7 +127,7 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info, *param_charp = data->info.contenttype; break; case CURLINFO_PRIVATE: - *param_charp = (char *)data->set.private_data; + *param_charp = (const char *)data->set.private_data; break; case CURLINFO_FTP_ENTRY_PATH: /* Return the entrypath string from the most recent connection. @@ -281,7 +281,12 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, *param_longp = data->state.os_errno; break; case CURLINFO_NUM_CONNECTS: - *param_longp = data->info.numconnects; +#if SIZEOF_LONG < SIZEOF_CURL_OFF_T + if(data->info.numconnects > LONG_MAX) + *param_longp = LONG_MAX; + else +#endif + *param_longp = (long)data->info.numconnects; break; case CURLINFO_LASTSOCKET: sockfd = Curl_getconnectinfo(data, NULL); @@ -374,7 +379,7 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, return CURLE_OK; } -#define DOUBLE_SECS(x) (double)(x) / 1000000 +#define DOUBLE_SECS(x) ((double)(x) / 1000000) static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, curl_off_t *param_offt) diff --git a/vendor/curl/lib/gopher.c b/vendor/curl/lib/gopher.c index 186530d..71ff392 100644 --- a/vendor/curl/lib/gopher.c +++ b/vendor/curl/lib/gopher.c @@ -22,87 +22,21 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "gopher.h" #ifndef CURL_DISABLE_GOPHER -#include "urldata.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" #include "cfilters.h" #include "connect.h" -#include "gopher.h" #include "select.h" #include "url.h" #include "escape.h" -/* - * Forward declarations. - */ - -static CURLcode gopher_do(struct Curl_easy *data, bool *done); -#ifdef USE_SSL -static CURLcode gopher_connect(struct Curl_easy *data, bool *done); -static CURLcode gopher_connecting(struct Curl_easy *data, bool *done); -#endif - -/* - * Gopher protocol handler. - * This is also a nice simple template to build off for simple - * connect-command-download protocols. - */ - -const struct Curl_handler Curl_handler_gopher = { - "gopher", /* scheme */ - ZERO_NULL, /* setup_connection */ - gopher_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_GOPHER, /* defport */ - CURLPROTO_GOPHER, /* protocol */ - CURLPROTO_GOPHER, /* family */ - PROTOPT_NONE /* flags */ -}; - #ifdef USE_SSL -const struct Curl_handler Curl_handler_gophers = { - "gophers", /* scheme */ - ZERO_NULL, /* setup_connection */ - gopher_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - gopher_connect, /* connect_it */ - gopher_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_GOPHER, /* defport */ - CURLPROTO_GOPHERS, /* protocol */ - CURLPROTO_GOPHER, /* family */ - PROTOPT_SSL /* flags */ -}; - static CURLcode gopher_connect(struct Curl_easy *data, bool *done) { (void)data; @@ -129,8 +63,8 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; char *gopherpath; - char *path = data->state.up.path; - char *query = data->state.up.query; + const char *path = data->state.up.path; + const char *query = data->state.up.query; const char *buf = NULL; char *buf_alloc = NULL; size_t nwritten, buf_len; @@ -157,7 +91,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) curlx_free(gopherpath); } else { - char *newp; + const char *newp; /* Otherwise, drop / and the first character (i.e., item type) ... */ newp = gopherpath; @@ -191,7 +125,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) else break; - timeout_ms = Curl_timeleft_ms(data, FALSE); + timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; break; @@ -231,4 +165,79 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); return CURLE_OK; } + +/* + * Gopher protocol handler. + * This is also a nice simple template to build off for simple + * connect-command-download protocols. + */ + +static const struct Curl_protocol Curl_protocol_gopher = { + ZERO_NULL, /* setup_connection */ + gopher_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#ifdef USE_SSL +static const struct Curl_protocol Curl_protocol_gophers = { + ZERO_NULL, /* setup_connection */ + gopher_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + gopher_connect, /* connect_it */ + gopher_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; +#endif + #endif /* CURL_DISABLE_GOPHER */ + +const struct Curl_scheme Curl_scheme_gopher = { + "gopher", /* scheme */ +#ifdef CURL_DISABLE_GOPHER + ZERO_NULL, +#else + &Curl_protocol_gopher, +#endif + CURLPROTO_GOPHER, /* protocol */ + CURLPROTO_GOPHER, /* family */ + PROTOPT_NONE, /* flags */ + PORT_GOPHER, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_gophers = { + "gophers", /* scheme */ +#if defined(CURL_DISABLE_GOPHER) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_gophers, +#endif + CURLPROTO_GOPHERS, /* protocol */ + CURLPROTO_GOPHER, /* family */ + PROTOPT_SSL, /* flags */ + PORT_GOPHER, /* defport */ +}; diff --git a/vendor/curl/lib/gopher.h b/vendor/curl/lib/gopher.h index 1e6a5d2..bc977bc 100644 --- a/vendor/curl/lib/gopher.h +++ b/vendor/curl/lib/gopher.h @@ -23,11 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef CURL_DISABLE_GOPHER -extern const struct Curl_handler Curl_handler_gopher; -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_gophers; -#endif -#endif +extern const struct Curl_scheme Curl_scheme_gopher; +extern const struct Curl_scheme Curl_scheme_gophers; #endif /* HEADER_CURL_GOPHER_H */ diff --git a/vendor/curl/lib/hash.c b/vendor/curl/lib/hash.c index 0b6ecc4..8095ca2 100644 --- a/vendor/curl/lib/hash.c +++ b/vendor/curl/lib/hash.c @@ -155,7 +155,7 @@ static void hash_elem_link(struct Curl_hash *h, ++h->size; } -#define CURL_HASH_SLOT(x, y, z) x->table[x->hash_func(y, z, x->slots)] +#define CURL_HASH_SLOT(x, y, z) x->table[(x)->hash_func(y, z, (x)->slots)] #define CURL_HASH_SLOT_ADDR(x, y, z) &CURL_HASH_SLOT(x, y, z) void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, @@ -177,7 +177,7 @@ void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, if(h->comp_func(he->key, he->key_len, key, key_len)) { /* existing key entry, overwrite by clearing old pointer */ hash_elem_clear_ptr(h, he); - he->ptr = (void *)p; + he->ptr = p; he->dtor = dtor; return p; } diff --git a/vendor/curl/lib/hash.h b/vendor/curl/lib/hash.h index 8bf47cc..267f91d 100644 --- a/vendor/curl/lib/hash.h +++ b/vendor/curl/lib/hash.h @@ -85,7 +85,7 @@ void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p); void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, Curl_hash_elem_dtor dtor); int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len); -void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len); +void *Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len); void Curl_hash_destroy(struct Curl_hash *h); size_t Curl_hash_count(struct Curl_hash *h); diff --git a/vendor/curl/lib/headers.c b/vendor/curl/lib/headers.c index c188045..5a6b120 100644 --- a/vendor/curl/lib/headers.c +++ b/vendor/curl/lib/headers.c @@ -326,7 +326,7 @@ CURLcode Curl_headers_init(struct Curl_easy *data) struct Curl_cwriter *writer; CURLcode result; - if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) { + if(data->conn && (data->conn->scheme->protocol & PROTO_FAMILY_HTTP)) { /* avoid installing it twice */ if(Curl_cwriter_get_by_name(data, hds_cw_collect.name)) return CURLE_OK; diff --git a/vendor/curl/lib/headers.h b/vendor/curl/lib/headers.h index adb03af..85905e5 100644 --- a/vendor/curl/lib/headers.h +++ b/vendor/curl/lib/headers.h @@ -30,7 +30,7 @@ struct Curl_header_store { struct Curl_llist_node node; char *name; /* points into 'buffer' */ - char *value; /* points into 'buffer */ + char *value; /* points into 'buffer' */ int request; /* 0 is the first request, then 1.. 2.. */ unsigned char type; /* CURLH_* defines */ char buffer[1]; /* this is the raw header blob */ @@ -54,9 +54,10 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, CURLcode Curl_headers_cleanup(struct Curl_easy *data); #else -#define Curl_headers_init(x) CURLE_OK -#define Curl_headers_push(x,y,z,a) CURLE_OK -#define Curl_headers_cleanup(x) Curl_nop_stmt +#define Curl_headers_init(x) CURLE_OK +#define Curl_headers_push(x, y, z, a) \ + ((void)(x), (void)(y), (void)(z), (void)(a), CURLE_OK) +#define Curl_headers_cleanup(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_HEADER_H */ diff --git a/vendor/curl/lib/hmac.c b/vendor/curl/lib/hmac.c index 9080588..028b476 100644 --- a/vendor/curl/lib/hmac.c +++ b/vendor/curl/lib/hmac.c @@ -52,7 +52,7 @@ struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams, unsigned char b; /* Create HMAC context. */ - i = sizeof(*ctxt) + 2 * hashparams->ctxtsize + hashparams->resultlen; + i = sizeof(*ctxt) + (2 * hashparams->ctxtsize) + hashparams->resultlen; ctxt = curlx_malloc(i); if(!ctxt) @@ -99,11 +99,11 @@ struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams, } int Curl_HMAC_update(struct HMAC_context *ctxt, - const unsigned char *ptr, + const unsigned char *data, unsigned int len) { /* Update first hash calculation. */ - ctxt->hash->hupdate(ctxt->hashctxt1, ptr, len); + ctxt->hash->hupdate(ctxt->hashctxt1, data, len); return 0; } @@ -143,7 +143,7 @@ int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output) */ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, - const unsigned char *buf, const size_t buflen, + const unsigned char *data, const size_t datalen, unsigned char *output) { struct HMAC_context *ctxt = @@ -153,7 +153,7 @@ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, return CURLE_OUT_OF_MEMORY; /* Update the digest with the given challenge */ - Curl_HMAC_update(ctxt, buf, curlx_uztoui(buflen)); + Curl_HMAC_update(ctxt, data, curlx_uztoui(datalen)); /* Finalise the digest */ Curl_HMAC_final(ctxt, output); diff --git a/vendor/curl/lib/hostip.c b/vendor/curl/lib/hostip.c index a0e1d40..542b655 100644 --- a/vendor/curl/lib/hostip.c +++ b/vendor/curl/lib/hostip.c @@ -44,10 +44,12 @@ #include #include "urldata.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "connect.h" #include "hostip.h" #include "hash.h" +#include "httpsrr.h" #include "rand.h" #include "curl_share.h" #include "url.h" @@ -112,15 +114,85 @@ * CURLRES_* defines based on the config*.h and curl_setup.h defines. */ -static void dnscache_entry_free(struct Curl_dns_entry *dns); - -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static void show_resolve_info(struct Curl_easy *data, - struct Curl_dns_entry *dns); + struct Curl_dns_entry *dns) +{ + const struct Curl_addrinfo *a; + CURLcode result = CURLE_OK; +#ifdef CURLRES_IPV6 + struct dynbuf out[2]; +#else + struct dynbuf out[1]; +#endif + DEBUGASSERT(data); + DEBUGASSERT(dns); + + if(!data->set.verbose || + /* ignore no name or numerical IP addresses */ + !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) + return; + + a = dns->addr; + + infof(data, "Host %s:%d was resolved.", + (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport); + + curlx_dyn_init(&out[0], 1024); +#ifdef CURLRES_IPV6 + curlx_dyn_init(&out[1], 1024); +#endif + + while(a) { + if( +#ifdef CURLRES_IPV6 + a->ai_family == PF_INET6 || +#endif + a->ai_family == PF_INET) { + char buf[MAX_IPADR_LEN]; + struct dynbuf *d = &out[(a->ai_family != PF_INET)]; + Curl_printable_address(a, buf, sizeof(buf)); + if(curlx_dyn_len(d)) + result = curlx_dyn_addn(d, ", ", 2); + if(!result) + result = curlx_dyn_add(d, buf); + if(result) { + infof(data, "too many IP, cannot show"); + goto fail; + } + } + a = a->ai_next; + } + +#ifdef CURLRES_IPV6 + infof(data, "IPv6: %s", + (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)")); +#endif + infof(data, "IPv4: %s", + (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)")); + +fail: + curlx_dyn_free(&out[0]); +#ifdef CURLRES_IPV6 + curlx_dyn_free(&out[1]); +#endif +} #else #define show_resolve_info(x, y) Curl_nop_stmt #endif +static void dnscache_entry_free(struct Curl_dns_entry *dns) +{ + Curl_freeaddrinfo(dns->addr); +#ifdef USE_HTTPSRR + if(dns->hinfo) { + Curl_httpsrr_cleanup(dns->hinfo); + curlx_free(dns->hinfo); + } +#endif + curlx_free(dns); +} + /* * Curl_printable_address() stores a printable version of the 1st address * given in the 'ai' argument. The result will be stored in the buf that is @@ -487,20 +559,20 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, struct Curl_dns_entry * Curl_dnscache_mk_entry(struct Curl_easy *data, - struct Curl_addrinfo *addr, + struct Curl_addrinfo **paddr, const char *hostname, size_t hostlen, /* length or zero */ int port, bool permanent) { - struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns = NULL; #ifndef CURL_DISABLE_SHUFFLE_DNS /* shuffle addresses if requested */ - if(data->set.dns_shuffle_addresses) { - CURLcode result = Curl_shuffle_addr(data, &addr); + if(data->set.dns_shuffle_addresses && paddr) { + CURLcode result = Curl_shuffle_addr(data, paddr); if(result) - return NULL; + goto out; } #else (void)data; @@ -511,10 +583,10 @@ Curl_dnscache_mk_entry(struct Curl_easy *data, /* Create a new cache entry */ dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen); if(!dns) - return NULL; + goto out; dns->refcount = 1; /* the cache has the first reference */ - dns->addr = addr; /* this is the address(es) */ + dns->addr = paddr ? *paddr : NULL; /* this is the address(es) */ if(permanent) { dns->timestamp.tv_sec = 0; /* an entry that never goes stale */ dns->timestamp.tv_usec = 0; /* an entry that never goes stale */ @@ -526,13 +598,19 @@ Curl_dnscache_mk_entry(struct Curl_easy *data, if(hostlen) memcpy(dns->hostname, hostname, hostlen); +out: + if(paddr) { + if(!dns) + Curl_freeaddrinfo(*paddr); + *paddr = NULL; + } return dns; } static struct Curl_dns_entry * dnscache_add_addr(struct Curl_easy *data, struct Curl_dnscache *dnscache, - struct Curl_addrinfo *addr, + struct Curl_addrinfo **paddr, const char *hostname, size_t hlen, /* length or zero */ int port, @@ -543,7 +621,7 @@ dnscache_add_addr(struct Curl_easy *data, struct Curl_dns_entry *dns; struct Curl_dns_entry *dns2; - dns = Curl_dnscache_mk_entry(data, addr, hostname, hlen, port, permanent); + dns = Curl_dnscache_mk_entry(data, paddr, hostname, hlen, port, permanent); if(!dns) return NULL; @@ -555,7 +633,6 @@ dnscache_add_addr(struct Curl_easy *data, dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, (void *)dns); if(!dns2) { - dns->addr = NULL; dnscache_entry_free(dns); return NULL; } @@ -669,36 +746,33 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name) } #ifdef USE_IPV6 +/* the nature of most systems is that IPv6 status does not come and go during a + program's lifetime so we only probe the first time and then we have the + info kept for fast reuse */ +CURLcode Curl_probeipv6(struct Curl_multi *multi) +{ + /* probe to see if we have a working IPv6 stack */ + curl_socket_t s = CURL_SOCKET(PF_INET6, SOCK_DGRAM, 0); + multi->ipv6_works = FALSE; + if(s == CURL_SOCKET_BAD) { + if(SOCKERRNO == SOCKENOMEM) + return CURLE_OUT_OF_MEMORY; + } + else { + multi->ipv6_works = TRUE; + sclose(s); + } + return CURLE_OK; +} + /* * Curl_ipv6works() returns TRUE if IPv6 seems to work. */ bool Curl_ipv6works(struct Curl_easy *data) { - if(data) { - /* the nature of most system is that IPv6 status does not come and go - during a program's lifetime so we only probe the first time and then we - have the info kept for fast reuse */ - DEBUGASSERT(data); - DEBUGASSERT(data->multi); - if(data->multi->ipv6_up == IPV6_UNKNOWN) { - bool works = Curl_ipv6works(NULL); - data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD; - } - return data->multi->ipv6_up == IPV6_WORKS; - } - else { - int ipv6_works = -1; - /* probe to see if we have a working IPv6 stack */ - curl_socket_t s = CURL_SOCKET(PF_INET6, SOCK_DGRAM, 0); - if(s == CURL_SOCKET_BAD) - /* an IPv6 address was requested but we cannot get/use one */ - ipv6_works = 0; - else { - ipv6_works = 1; - sclose(s); - } - return ipv6_works > 0; - } + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + return data ? data->multi->ipv6_works : FALSE; } #endif /* USE_IPV6 */ @@ -908,13 +982,9 @@ CURLcode Curl_resolv(struct Curl_easy *data, } else if(addr) { /* we got a response, create a dns entry, add to cache, return */ - dns = Curl_dnscache_mk_entry(data, addr, hostname, 0, port, FALSE); + dns = Curl_dnscache_mk_entry(data, &addr, hostname, 0, port, FALSE); if(!dns || Curl_dnscache_add(data, dns)) { /* this is OOM or similar, do not store such negative resolves */ - Curl_freeaddrinfo(addr); - if(dns) - /* avoid a dangling pointer to addr in the dying dns entry */ - dns->addr = NULL; result = CURLE_OUT_OF_MEMORY; goto error; } @@ -923,10 +993,12 @@ CURLcode Curl_resolv(struct Curl_easy *data, return CURLE_OK; } else if(respwait) { +#ifdef USE_CURL_ASYNC if(!Curl_resolv_check(data, &dns)) { *entry = dns; return dns ? CURLE_OK : CURLE_AGAIN; } +#endif result = CURLE_COULDNT_RESOLVE_HOST; } error: @@ -943,20 +1015,20 @@ CURLcode Curl_resolv_blocking(struct Curl_easy *data, const char *hostname, int port, int ip_version, - struct Curl_dns_entry **dnsentry) + struct Curl_dns_entry **entry) { CURLcode result; DEBUGASSERT(hostname && *hostname); - *dnsentry = NULL; - result = Curl_resolv(data, hostname, port, ip_version, FALSE, dnsentry); + *entry = NULL; + result = Curl_resolv(data, hostname, port, ip_version, FALSE, entry); switch(result) { case CURLE_OK: - DEBUGASSERT(*dnsentry); + DEBUGASSERT(*entry); return CURLE_OK; case CURLE_AGAIN: - DEBUGASSERT(!*dnsentry); - result = Curl_async_await(data, dnsentry); - if(result || !*dnsentry) { + DEBUGASSERT(!*entry); + result = Curl_async_await(data, entry); + if(result || !*entry) { /* close the connection, since we cannot return failure here without cleaning up this connection properly. */ connclose(data->conn, "async resolve failed"); @@ -1160,18 +1232,6 @@ CURLcode Curl_resolv_timeout(struct Curl_easy *data, return result; } -static void dnscache_entry_free(struct Curl_dns_entry *dns) -{ - Curl_freeaddrinfo(dns->addr); -#ifdef USE_HTTPSRR - if(dns->hinfo) { - Curl_httpsrr_cleanup(dns->hinfo); - curlx_free(dns->hinfo); - } -#endif - curlx_free(dns); -} - /* * Curl_resolv_unlink() releases a reference to the given cached DNS entry. * When the reference count reaches 0, the entry is destroyed. It is important @@ -1266,12 +1326,10 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) struct Curl_addrinfo *head = NULL, *tail = NULL; size_t entry_len; char address[64]; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *addresses = NULL; -#endif curl_off_t port = 0; bool permanent = TRUE; bool error = TRUE; + VERBOSE(const char *addresses = NULL); if(*host == '+') { host++; @@ -1291,9 +1349,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) curlx_str_single(&host, ':')) goto err; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - addresses = host; -#endif + VERBOSE(addresses = host); /* start the address section */ while(*host) { @@ -1310,7 +1366,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(curlx_str_until(&host, &target, 4096, ',')) { if(curlx_str_single(&host, ',')) goto err; - /* survive nothing but just a comma */ + /* survive nothing but a comma */ continue; } } @@ -1387,25 +1443,21 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) } /* put this new host in the cache */ - dns = dnscache_add_addr(data, dnscache, head, curlx_str(&source), + dns = dnscache_add_addr(data, dnscache, &head, curlx_str(&source), curlx_strlen(&source), (int)port, permanent); if(dns) /* release the returned reference; the cache itself will keep the * entry alive: */ dns->refcount--; - else - Curl_freeaddrinfo(head); dnscache_unlock(data, dnscache); if(!dns) return CURLE_OUT_OF_MEMORY; -#ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s", (int)curlx_strlen(&source), curlx_str(&source), port, addresses, permanent ? "" : " (non-permanent)"); -#endif /* Wildcard hostname */ if(curlx_str_casecompare(&source, "*")) { @@ -1420,71 +1472,6 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) return CURLE_OK; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void show_resolve_info(struct Curl_easy *data, - struct Curl_dns_entry *dns) -{ - struct Curl_addrinfo *a; - CURLcode result = CURLE_OK; -#ifdef CURLRES_IPV6 - struct dynbuf out[2]; -#else - struct dynbuf out[1]; -#endif - DEBUGASSERT(data); - DEBUGASSERT(dns); - - if(!data->set.verbose || - /* ignore no name or numerical IP addresses */ - !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) - return; - - a = dns->addr; - - infof(data, "Host %s:%d was resolved.", - (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport); - - curlx_dyn_init(&out[0], 1024); -#ifdef CURLRES_IPV6 - curlx_dyn_init(&out[1], 1024); -#endif - - while(a) { - if( -#ifdef CURLRES_IPV6 - a->ai_family == PF_INET6 || -#endif - a->ai_family == PF_INET) { - char buf[MAX_IPADR_LEN]; - struct dynbuf *d = &out[(a->ai_family != PF_INET)]; - Curl_printable_address(a, buf, sizeof(buf)); - if(curlx_dyn_len(d)) - result = curlx_dyn_addn(d, ", ", 2); - if(!result) - result = curlx_dyn_add(d, buf); - if(result) { - infof(data, "too many IP, cannot show"); - goto fail; - } - } - a = a->ai_next; - } - -#ifdef CURLRES_IPV6 - infof(data, "IPv6: %s", - (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)")); -#endif - infof(data, "IPv4: %s", - (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)")); - -fail: - curlx_dyn_free(&out[0]); -#ifdef CURLRES_IPV6 - curlx_dyn_free(&out[1]); -#endif -} -#endif - #ifdef USE_CURL_ASYNC CURLcode Curl_resolv_check(struct Curl_easy *data, struct Curl_dns_entry **dns) diff --git a/vendor/curl/lib/hostip.h b/vendor/curl/lib/hostip.h index 712b8ea..6c7f129 100644 --- a/vendor/curl/lib/hostip.h +++ b/vendor/curl/lib/hostip.h @@ -26,14 +26,7 @@ #include "curl_setup.h" #include "hash.h" -#include "curl_addrinfo.h" #include "curlx/timeval.h" /* for curltime, timediff_t */ -#include "asyn.h" -#include "httpsrr.h" - -#ifdef USE_HTTPSRR -# include -#endif /* Allocate enough memory to hold the full name information structs and * everything. OSF1 is known to require at least 8872 bytes. The buffer @@ -50,6 +43,8 @@ struct hostent; struct Curl_easy; struct connectdata; struct easy_pollset; +struct Curl_https_rrinfo; +struct Curl_multi; enum alpnid { ALPN_none = 0, @@ -91,26 +86,30 @@ CURLcode Curl_resolv(struct Curl_easy *data, int port, int ip_version, bool allowDOH, - struct Curl_dns_entry **dnsentry); + struct Curl_dns_entry **entry); CURLcode Curl_resolv_blocking(struct Curl_easy *data, const char *hostname, int port, int ip_version, - struct Curl_dns_entry **dnsentry); + struct Curl_dns_entry **entry); CURLcode Curl_resolv_timeout(struct Curl_easy *data, const char *hostname, int port, int ip_version, - struct Curl_dns_entry **dnsentry, + struct Curl_dns_entry **entry, timediff_t timeoutms); #ifdef USE_IPV6 + +/* probe if it seems to work */ +CURLcode Curl_probeipv6(struct Curl_multi *multi); /* * Curl_ipv6works() returns TRUE if IPv6 seems to work. */ bool Curl_ipv6works(struct Curl_easy *data); #else +#define Curl_probeipv6(x) CURLE_OK #define Curl_ipv6works(x) FALSE #endif @@ -119,7 +118,7 @@ void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns); /* init a new dns cache */ -void Curl_dnscache_init(struct Curl_dnscache *dns, size_t hashsize); +void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size); void Curl_dnscache_destroy(struct Curl_dnscache *dns); @@ -134,14 +133,14 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); CURLcode Curl_once_resolved(struct Curl_easy *data, struct Curl_dns_entry *dns, - bool *protocol_connect); + bool *protocol_done); /* * Curl_printable_address() returns a printable version of the 1st address * given in the 'ip' argument. The result will be stored in the buf that is * bufsize bytes big. */ -void Curl_printable_address(const struct Curl_addrinfo *ip, +void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, size_t bufsize); /* @@ -152,13 +151,14 @@ void Curl_printable_address(const struct Curl_addrinfo *ip, * The entry is created with a reference count of 1. * Use `Curl_resolv_unlink()` to release your hold on it. * - * The call takes ownership of `addr`and makes a copy of `hostname`. + * The call takes ownership of `addr`, even in case of failure, and always + * clears `*paddr`. It makes a copy of `hostname`. * * Returns entry or NULL on OOM. */ struct Curl_dns_entry * Curl_dnscache_mk_entry(struct Curl_easy *data, - struct Curl_addrinfo *addr, + struct Curl_addrinfo **paddr, const char *hostname, size_t hostlen, /* length or zero */ int port, diff --git a/vendor/curl/lib/hostip4.c b/vendor/curl/lib/hostip4.c index 422e0b2..f8cfac1 100644 --- a/vendor/curl/lib/hostip4.c +++ b/vendor/curl/lib/hostip4.c @@ -43,6 +43,7 @@ #endif #include "urldata.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" #include "url.h" @@ -74,9 +75,6 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, struct Curl_addrinfo *ai = NULL; (void)ip_version; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif ai = Curl_ipv4_resolve_r(hostname, port); if(!ai) diff --git a/vendor/curl/lib/hostip6.c b/vendor/curl/lib/hostip6.c index e4793d7..8aa1974 100644 --- a/vendor/curl/lib/hostip6.c +++ b/vendor/curl/lib/hostip6.c @@ -44,6 +44,7 @@ #include "urldata.h" #include "cfilters.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "hostip.h" #include "url.h" @@ -52,23 +53,6 @@ #ifdef CURLRES_SYNCH -#ifdef DEBUG_ADDRINFO -static void dump_addrinfo(const struct Curl_addrinfo *ai) -{ - curl_mprintf("dump_addrinfo:\n"); - for(; ai; ai = ai->ai_next) { - char buf[INET6_ADDRSTRLEN]; - curl_mprintf(" fam %2d, CNAME %s, ", - ai->ai_family, - ai->ai_canonname ? ai->ai_canonname : ""); - Curl_printable_address(ai, buf, sizeof(buf)); - curl_mprintf("%s\n", buf); - } -} -#else -#define dump_addrinfo(x) Curl_nop_stmt -#endif - /* * Curl_sync_getaddrinfo() when built IPv6-enabled (non-threading and * non-ares version). @@ -130,8 +114,6 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, Curl_addrinfo_set_port(res, port); } - dump_addrinfo(res); - return res; } #endif /* CURLRES_SYNCH */ diff --git a/vendor/curl/lib/hsts.c b/vendor/curl/lib/hsts.c index de6c573..03f51f8 100644 --- a/vendor/curl/lib/hsts.c +++ b/vendor/curl/lib/hsts.c @@ -206,7 +206,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, /* check if it already exists */ sts = Curl_hsts(h, hostname, hlen, FALSE); if(sts) { - /* just update these fields */ + /* update these fields */ sts->expires = expires; sts->includeSubDomains = subdomains; } @@ -294,7 +294,7 @@ static CURLcode hsts_push(struct Curl_easy *data, stamp.tm_hour, stamp.tm_min, stamp.tm_sec); } else - curlx_strcopy(e.expire, sizeof(e.expire), UNLIMITED, strlen(UNLIMITED)); + curlx_strcopy(e.expire, sizeof(e.expire), STRCONST(UNLIMITED)); sc = data->set.hsts_write(data, &e, i, data->set.hsts_write_userp); *stop = (sc != CURLSTS_OK); @@ -318,7 +318,7 @@ static CURLcode hsts_out(struct stsentry *sts, FILE *fp) } else curl_mfprintf(fp, "%s%s \"%s\"\n", - sts->includeSubDomains ? ".": "", sts->host, UNLIMITED); + sts->includeSubDomains ? "." : "", sts->host, UNLIMITED); return CURLE_OK; } @@ -456,7 +456,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) e.namelen = sizeof(buffer) - 1; e.includeSubDomains = FALSE; /* default */ e.expire[0] = 0; - e.name[0] = 0; /* just to make it clean */ + e.name[0] = 0; /* to make it clean */ sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp); if(sc == CURLSTS_OK) { time_t expires = 0; diff --git a/vendor/curl/lib/hsts.h b/vendor/curl/lib/hsts.h index e2c9922..e0f6363 100644 --- a/vendor/curl/lib/hsts.h +++ b/vendor/curl/lib/hsts.h @@ -49,7 +49,7 @@ struct hsts { struct hsts *Curl_hsts_init(void); void Curl_hsts_cleanup(struct hsts **hp); CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, - const char *sts); + const char *header); struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, size_t hlen, bool subdomain); CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, diff --git a/vendor/curl/lib/http.c b/vendor/curl/lib/http.c index a78e2f6..188da5f 100644 --- a/vendor/curl/lib/http.c +++ b/vendor/curl/lib/http.c @@ -22,6 +22,7 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_HTTP @@ -46,7 +47,6 @@ #include #endif -#include "urldata.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" @@ -77,7 +77,7 @@ #include "http2.h" #include "cfilters.h" #include "connect.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "altsvc.h" #include "hsts.h" #include "rtsp.h" @@ -85,94 +85,6 @@ #include "bufref.h" #include "curlx/strparse.h" -/* - * Forward declarations. - */ - -static bool http_should_fail(struct Curl_easy *data, int httpcode); -static bool http_exp100_is_waiting(struct Curl_easy *data); -static CURLcode http_exp100_add_reader(struct Curl_easy *data); -static void http_exp100_send_anyway(struct Curl_easy *data); -static bool http_exp100_is_selected(struct Curl_easy *data); -static void http_exp100_got100(struct Curl_easy *data); -static CURLcode http_firstwrite(struct Curl_easy *data); -static CURLcode http_header(struct Curl_easy *data, - const char *hd, size_t hdlen); -static CURLcode http_range(struct Curl_easy *data, - Curl_HttpReq httpreq); -static CURLcode http_req_set_TE(struct Curl_easy *data, - struct dynbuf *req, - int httpversion); -static CURLcode http_size(struct Curl_easy *data); -static CURLcode http_statusline(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req); -static CURLcode http_useragent(struct Curl_easy *data); -static CURLcode http_write_header(struct Curl_easy *data, - const char *hd, size_t hdlen); - -/* - * HTTP handler interface. - */ -const struct Curl_handler Curl_handler_http = { - "http", /* scheme */ - Curl_http_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - Curl_http_doing_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - Curl_http_perform_pollset, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - Curl_http_write_resp, /* write_resp */ - Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - Curl_http_follow, /* follow */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTP, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE - -}; - -#ifdef USE_SSL -/* - * HTTPS handler interface. - */ -const struct Curl_handler Curl_handler_https = { - "https", /* scheme */ - Curl_http_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - NULL, /* connecting */ - ZERO_NULL, /* doing */ - NULL, /* proto_pollset */ - Curl_http_doing_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - Curl_http_perform_pollset, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - Curl_http_write_resp, /* write_resp */ - Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - Curl_http_follow, /* follow */ - PORT_HTTPS, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE -}; - -#endif - void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg) { memset(neg, 0, sizeof(*neg)); @@ -252,9 +164,6 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, return NULL; } -#else -/* disabled */ -#define Curl_checkProxyheaders(x, y, z, a) NULL #endif static bool http_header_is_empty(const char *header) @@ -288,7 +197,7 @@ static CURLcode copy_custom_value(const char *header, char **valp) curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE); curlx_str_trimblanks(&out); - *valp = Curl_memdup0(curlx_str(&out), curlx_strlen(&out)); + *valp = curlx_memdup0(curlx_str(&out), curlx_strlen(&out)); if(*valp) return CURLE_OK; return CURLE_OUT_OF_MEMORY; @@ -315,7 +224,7 @@ char *Curl_copy_header_value(const char *header) !curlx_str_single(&header, ':')) { curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE); curlx_str_trimblanks(&out); - return Curl_memdup0(curlx_str(&out), curlx_strlen(&out)); + return curlx_memdup0(curlx_str(&out), curlx_strlen(&out)); } /* bad input, should never happen */ DEBUGASSERT(0); @@ -419,7 +328,7 @@ static CURLcode http_output_bearer(struct Curl_easy *data) #endif -/* pickoneauth() selects the most favourable authentication method from the +/* pickoneauth() selects the most favorable authentication method from the * ones available and the ones we want. * * return TRUE if one was picked @@ -482,10 +391,10 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, * amount remains. This may be overridden by authentications further * below! */ bool abort_upload = (!data->req.upload_done && !little_upload_remains); - const char *ongoing_auth = NULL; + VERBOSE(const char *ongoing_auth = NULL); /* We need a rewind before uploading client read data again. The - * checks below just influence of the upload is to be continued + * checks below influence of the upload is to be continued * or aborted early. * This depends on how much remains to be sent and in what state * the authentication is. Some auth schemes such as NTLM do not work @@ -504,7 +413,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, #ifdef USE_NTLM if((data->state.authproxy.picked == CURLAUTH_NTLM) || (data->state.authhost.picked == CURLAUTH_NTLM)) { - ongoing_auth = "NTLM"; + VERBOSE(ongoing_auth = "NTLM"); if((conn->http_ntlm_state != NTLMSTATE_NONE) || (conn->proxy_ntlm_state != NTLMSTATE_NONE)) { /* The NTLM-negotiation has started, keep on sending. @@ -517,7 +426,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, /* There is still data left to send */ if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) || (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) { - ongoing_auth = "NEGOTIATE"; + VERBOSE(ongoing_auth = "NEGOTIATE"); if((conn->http_negotiate_state != GSS_AUTHNONE) || (conn->proxy_negotiate_state != GSS_AUTHNONE)) { /* The NEGOTIATE-negotiation has started, keep on sending. @@ -547,20 +456,90 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, return CURLE_OK; } +/** + * http_should_fail() determines whether an HTTP response code has gotten us + * into an error state or not. + * + * @retval FALSE communications should continue + * + * @retval TRUE communications should not continue + */ +static bool http_should_fail(struct Curl_easy *data, int httpcode) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + + /* + ** If we have not been asked to fail on error, + ** do not fail. + */ + if(!data->set.http_fail_on_error) + return FALSE; + + /* + ** Any code < 400 is never terminal. + */ + if(httpcode < 400) + return FALSE; + + /* + ** A 416 response to a resume request is presumably because the file is + ** already completely downloaded and thus not actually a fail. + */ + if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && + httpcode == 416) + return FALSE; + + /* + ** Any code >= 400 that is not 401 or 407 is always + ** a terminal error + */ + if((httpcode != 401) && (httpcode != 407)) + return TRUE; + + /* + ** All we have left to deal with is 401 and 407 + */ + DEBUGASSERT((httpcode == 401) || (httpcode == 407)); + + /* + ** Examine the current authentication state to see if this is an error. The + ** idea is for this function to get called after processing all the headers + ** in a response message. So, if we have been to asked to authenticate a + ** particular stage, and we have done it, we are OK. If we are already + ** completely authenticated, it is not OK to get another 401 or 407. + ** + ** It is possible for authentication to go stale such that the client needs + ** to reauthenticate. Once that info is available, use it here. + */ + + /* + ** Either we are not authenticating, or we are supposed to be authenticating + ** something else. This is an error. + */ + if((httpcode == 401) && !data->state.aptr.user) + return TRUE; +#ifndef CURL_DISABLE_PROXY + if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) + return TRUE; +#endif + + return (bool)data->state.authproblem; +} + /* * Curl_http_auth_act() gets called when all HTTP headers have been received * and it checks what authentication methods that are available and decides * which one (if any) to use. It will set 'newurl' if an auth method was * picked. */ - CURLcode Curl_http_auth_act(struct Curl_easy *data) { struct connectdata *conn = data->conn; bool pickhost = FALSE; bool pickproxy = FALSE; CURLcode result = CURLE_OK; - unsigned long authmask = ~0ul; + unsigned long authmask = ~0UL; if(!data->set.str[STRING_BEARER]) authmask &= (unsigned long)~CURLAUTH_BEARER; @@ -705,11 +684,12 @@ static CURLcode output_auth_headers(struct Curl_easy *data, /* Basic */ if( #ifndef CURL_DISABLE_PROXY - (proxy && conn->bits.proxy_user_passwd && - !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || + (proxy && conn->bits.proxy_user_passwd && + !Curl_checkProxyheaders(data, conn, + STRCONST("Proxy-authorization"))) || #endif - (!proxy && data->state.aptr.user && - !Curl_checkheaders(data, STRCONST("Authorization")))) { + (!proxy && data->state.aptr.user && + !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Basic"; result = http_output_basic(data, proxy); if(result) @@ -724,8 +704,9 @@ static CURLcode output_auth_headers(struct Curl_easy *data, #ifndef CURL_DISABLE_BEARER_AUTH if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ - if((!proxy && data->set.str[STRING_BEARER] && - !Curl_checkheaders(data, STRCONST("Authorization")))) { + if(!proxy && data->set.str[STRING_BEARER] && + Curl_auth_allowed_to_host(data) && + !Curl_checkheaders(data, STRCONST("Authorization"))) { auth = "Bearer"; result = http_output_bearer(data); if(result) @@ -824,7 +805,7 @@ Curl_http_output_auth(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY /* Send proxy authentication header if needed */ if(conn->bits.httpproxy && - (conn->bits.tunnel_proxy == (bit)proxytunnel)) { + (conn->bits.tunnel_proxy == (curl_bit)proxytunnel)) { result = output_auth_headers(data, conn, authproxy, request, path, TRUE); if(result) return result; @@ -898,7 +879,7 @@ static CURLcode auth_spnego(struct Curl_easy *data, bool proxy, const char *auth, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { if((authp->avail & CURLAUTH_NEGOTIATE) || Curl_auth_is_spnego_supported()) { *availp |= CURLAUTH_NEGOTIATE; @@ -931,7 +912,7 @@ static CURLcode auth_ntlm(struct Curl_easy *data, bool proxy, const char *auth, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { /* NTLM support requires the SSL crypto libs */ if((authp->avail & CURLAUTH_NTLM) || Curl_auth_is_ntlm_supported()) { @@ -960,7 +941,7 @@ static CURLcode auth_digest(struct Curl_easy *data, bool proxy, const char *auth, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { if(authp->avail & CURLAUTH_DIGEST) infof(data, "Ignoring duplicate digest auth header."); @@ -989,14 +970,13 @@ static CURLcode auth_digest(struct Curl_easy *data, #ifndef CURL_DISABLE_BASIC_AUTH static CURLcode auth_basic(struct Curl_easy *data, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { *availp |= CURLAUTH_BASIC; authp->avail |= CURLAUTH_BASIC; if(authp->picked == CURLAUTH_BASIC) { - /* We asked for Basic authentication but got a 40X back - anyway, which basically means our name+password is not - valid. */ + /* We asked for Basic authentication but got a 40X back anyway, which + means our name+password is not valid. */ authp->avail = CURLAUTH_NONE; infof(data, "Basic authentication problem, ignoring."); data->state.authproblem = TRUE; @@ -1008,13 +988,13 @@ static CURLcode auth_basic(struct Curl_easy *data, #ifndef CURL_DISABLE_BEARER_AUTH static CURLcode auth_bearer(struct Curl_easy *data, struct auth *authp, - unsigned long *availp) + uint32_t *availp) { *availp |= CURLAUTH_BEARER; authp->avail |= CURLAUTH_BEARER; if(authp->picked == CURLAUTH_BEARER) { - /* We asked for Bearer authentication but got a 40X back - anyway, which basically means our token is not valid. */ + /* We asked for Bearer authentication but got a 40X back anyway, which + means our token is not valid. */ authp->avail = CURLAUTH_NONE; infof(data, "Bearer authentication problem, ignoring."); data->state.authproblem = TRUE; @@ -1042,7 +1022,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, !defined(CURL_DISABLE_BASIC_AUTH) || \ !defined(CURL_DISABLE_BEARER_AUTH) - unsigned long *availp; + uint32_t *availp; struct auth *authp; CURLcode result = CURLE_OK; DEBUGASSERT(auth); @@ -1116,82 +1096,13 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, #endif } -/** - * http_should_fail() determines whether an HTTP response code has gotten us - * into an error state or not. - * - * @retval FALSE communications should continue - * - * @retval TRUE communications should not continue - */ -static bool http_should_fail(struct Curl_easy *data, int httpcode) -{ - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - - /* - ** If we have not been asked to fail on error, - ** do not fail. - */ - if(!data->set.http_fail_on_error) - return FALSE; - - /* - ** Any code < 400 is never terminal. - */ - if(httpcode < 400) - return FALSE; - - /* - ** A 416 response to a resume request is presumably because the file is - ** already completely downloaded and thus not actually a fail. - */ - if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && - httpcode == 416) - return FALSE; - - /* - ** Any code >= 400 that is not 401 or 407 is always - ** a terminal error - */ - if((httpcode != 401) && (httpcode != 407)) - return TRUE; - - /* - ** All we have left to deal with is 401 and 407 - */ - DEBUGASSERT((httpcode == 401) || (httpcode == 407)); - - /* - ** Examine the current authentication state to see if this is an error. The - ** idea is for this function to get called after processing all the headers - ** in a response message. So, if we have been to asked to authenticate a - ** particular stage, and we have done it, we are OK. If we are already - ** completely authenticated, it is not OK to get another 401 or 407. - ** - ** It is possible for authentication to go stale such that the client needs - ** to reauthenticate. Once that info is available, use it here. - */ - - /* - ** Either we are not authenticating, or we are supposed to be authenticating - ** something else. This is an error. - */ - if((httpcode == 401) && !data->state.aptr.user) - return TRUE; -#ifndef CURL_DISABLE_PROXY - if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) - return TRUE; -#endif - - return data->state.authproblem; -} - static void http_switch_to_get(struct Curl_easy *data, int code) { const char *req = data->set.str[STRING_CUSTOMREQUEST]; + if((req || data->state.httpreq != HTTPREQ_GET) && (data->set.http_follow_mode == CURLFOLLOW_OBEYCODE)) { + NOVERBOSE((void)code); infof(data, "Switch to GET because of %d response", code); data->state.http_ignorecustom = TRUE; } @@ -1284,7 +1195,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, } /* the URL could not be parsed for some reason, but since this is FAKE - mode, just duplicate the field as-is */ + mode, duplicate the field as-is */ follow_url = curlx_strdup(newurl); if(!follow_url) return CURLE_OUT_OF_MEMORY; @@ -1325,14 +1236,14 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, } else { char *scheme; - const struct Curl_handler *p; + const struct Curl_scheme *p; uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); if(uc) { curlx_free(follow_url); return Curl_uc_to_curlcode(uc); } - p = Curl_get_scheme_handler(scheme); + p = Curl_get_scheme(scheme); if(p && (p->protocol != data->info.conn_protocol)) { infof(data, "Clear auth, redirects scheme from %s to %s", data->info.conn_scheme, scheme); @@ -1413,7 +1324,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, if((data->state.httpreq == HTTPREQ_POST || data->state.httpreq == HTTPREQ_POST_FORM || data->state.httpreq == HTTPREQ_POST_MIME) && - !(data->set.keep_post & CURL_REDIR_POST_301)) { + !data->set.post301) { http_switch_to_get(data, 301); switch_to_get = TRUE; } @@ -1438,7 +1349,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, if((data->state.httpreq == HTTPREQ_POST || data->state.httpreq == HTTPREQ_POST_FORM || data->state.httpreq == HTTPREQ_POST_MIME) && - !(data->set.keep_post & CURL_REDIR_POST_302)) { + !data->set.post302) { http_switch_to_get(data, 302); switch_to_get = TRUE; } @@ -1454,7 +1365,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, ((data->state.httpreq != HTTPREQ_POST && data->state.httpreq != HTTPREQ_POST_FORM && data->state.httpreq != HTTPREQ_POST_MIME) || - !(data->set.keep_post & CURL_REDIR_POST_303))) { + !data->set.post303)) { http_switch_to_get(data, 303); switch_to_get = TRUE; } @@ -1516,20 +1427,159 @@ bool Curl_compareheader(const char *headerline, /* line to check */ /* pass the header */ p = &headerline[hlen]; - if(curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE)) - return FALSE; - curlx_str_trimblanks(&val); + if(curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE)) + return FALSE; + curlx_str_trimblanks(&val); + + /* find the content string in the rest of the line */ + if(curlx_strlen(&val) >= clen) { + size_t len; + p = curlx_str(&val); + for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) { + if(curl_strnequal(p, content, clen)) + return TRUE; /* match! */ + } + } + return FALSE; /* no match */ +} + +struct cr_exp100_ctx { + struct Curl_creader super; + struct curltime start; /* time started waiting */ + enum expect100 state; +}; + +/* Expect: 100-continue client reader, blocking uploads */ + +static void http_exp100_continue(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + if(ctx->state > EXP100_SEND_DATA) { + ctx->state = EXP100_SEND_DATA; + Curl_expire_done(data, EXPIRE_100_TIMEOUT); + } +} + +static CURLcode cr_exp100_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *nread, bool *eos) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + timediff_t ms; + + switch(ctx->state) { + case EXP100_SENDING_REQUEST: + if(!Curl_req_sendbuf_empty(data)) { + /* The initial request data has not been fully sent yet. Do + * not start the timer yet. */ + DEBUGF(infof(data, "cr_exp100_read, request not full sent yet")); + *nread = 0; + *eos = FALSE; + return CURLE_OK; + } + /* We are now waiting for a reply from the server or + * a timeout on our side IFF the request has been fully sent. */ + DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, " + "timeout %dms", data->set.expect_100_timeout)); + ctx->state = EXP100_AWAITING_CONTINUE; + ctx->start = *Curl_pgrs_now(data); + Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); + *nread = 0; + *eos = FALSE; + return CURLE_OK; + case EXP100_FAILED: + DEBUGF(infof(data, "cr_exp100_read, expectation failed, error")); + *nread = 0; + *eos = FALSE; + return CURLE_READ_ERROR; + case EXP100_AWAITING_CONTINUE: + ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start); + if(ms < data->set.expect_100_timeout) { + DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); + *nread = 0; + *eos = FALSE; + return CURLE_OK; + } + /* we have waited long enough, continue anyway */ + http_exp100_continue(data, reader); + infof(data, "Done waiting for 100-continue"); + FALLTHROUGH(); + default: + DEBUGF(infof(data, "cr_exp100_read, pass through")); + return Curl_creader_read(data, reader->next, buf, blen, nread, eos); + } +} + +static void cr_exp100_done(struct Curl_easy *data, + struct Curl_creader *reader, int premature) +{ + struct cr_exp100_ctx *ctx = reader->ctx; + ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA; + Curl_expire_done(data, EXPIRE_100_TIMEOUT); +} + +static const struct Curl_crtype cr_exp100 = { + "cr-exp100", + Curl_creader_def_init, + cr_exp100_read, + Curl_creader_def_close, + Curl_creader_def_needs_rewind, + Curl_creader_def_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_cntrl, + Curl_creader_def_is_paused, + cr_exp100_done, + sizeof(struct cr_exp100_ctx) +}; + +static CURLcode http_exp100_add_reader(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL); + if(!result) + result = Curl_creader_add(data, reader); + if(!result) { + struct cr_exp100_ctx *ctx = reader->ctx; + ctx->state = EXP100_SENDING_REQUEST; + } + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +static void http_exp100_got100(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) + http_exp100_continue(data, r); +} + +static bool http_exp100_is_waiting(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) { + struct cr_exp100_ctx *ctx = r->ctx; + return ctx->state == EXP100_AWAITING_CONTINUE; + } + return FALSE; +} + +static void http_exp100_send_anyway(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + if(r) + http_exp100_continue(data, r); +} - /* find the content string in the rest of the line */ - if(curlx_strlen(&val) >= clen) { - size_t len; - p = curlx_str(&val); - for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) { - if(curl_strnequal(p, content, clen)) - return TRUE; /* match! */ - } - } - return FALSE; /* no match */ +static bool http_exp100_is_selected(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + return !!r; } /* this returns the socket to wait for in the DO and DOING state for the multi @@ -1559,6 +1609,33 @@ CURLcode Curl_http_perform_pollset(struct Curl_easy *data, return result; } +static CURLcode http_write_header(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + CURLcode result; + int writetype; + + /* now, only output this if the header AND body are requested: + */ + Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen); + + writetype = CLIENTWRITE_HEADER | + ((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0); + + result = Curl_client_write(data, writetype, hd, hdlen); + if(result) + return result; + + result = Curl_bump_headersize(data, hdlen, FALSE); + if(result) + return result; + + data->req.deductheadercount = (100 <= data->req.httpcode && + 199 >= data->req.httpcode) ? + data->req.headerbytecount : 0; + return result; +} + /* * Curl_http_done() gets called after a single HTTP request has been * performed. @@ -1590,7 +1667,7 @@ CURLcode Curl_http_done(struct Curl_easy *data, (data->req.bytecount + data->req.headerbytecount - data->req.deductheadercount) <= 0) { - /* If this connection is not simply closed to be retried, AND nothing was + /* If this connection is not closed to be retried, AND nothing was read from the HTTP server (that counts), this cannot be right so we return an error here */ failf(data, "Empty reply from server"); @@ -1853,11 +1930,11 @@ void Curl_http_method(struct Curl_easy *data, Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; const char *request; #ifndef CURL_DISABLE_WEBSOCKETS - if(data->conn->handler->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) + if(data->conn->scheme->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) httpreq = HTTPREQ_GET; else #endif - if((data->conn->handler->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_FTP)) && + if((data->conn->scheme->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_FTP)) && data->state.upload) httpreq = HTTPREQ_PUT; @@ -1922,7 +1999,7 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->handler->protocol; + data->state.first_remote_protocol = conn->scheme->protocol; } Curl_safefree(aptr->host); @@ -1948,7 +2025,7 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) if(*cookiehost == '[') { char *closingbracket; /* since the 'cookiehost' is an allocated memory area that will be - freed later we cannot simply increment the pointer */ + freed later we cannot increment the pointer */ memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1); closingbracket = strchr(cookiehost, ']'); if(closingbracket) @@ -2117,7 +2194,7 @@ static CURLcode set_post_reader(struct Curl_easy *data, Curl_HttpReq httpreq) switch(httpreq) { #ifndef CURL_DISABLE_MIME case HTTPREQ_POST_MIME: - data->state.mimepost = &data->set.mimepost; + data->state.mimepost = data->set.mimepostp; break; #endif #ifndef CURL_DISABLE_FORM_API @@ -2341,7 +2418,7 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, return CURLE_OK; /* For really small puts we do not use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make + the somewhat bigger ones we allow the app to disable it. Make sure that the expect100header is always set to the preferred value here. */ ptr = Curl_checkheaders(data, STRCONST("Expect")); @@ -2549,7 +2626,6 @@ static CURLcode http_range(struct Curl_easy *data, data->state.aptr.rangeline = curl_maprintf("Content-Range: bytes 0-%" FMT_OFF_T "/" "%" FMT_OFF_T "\r\n", req_clen - 1, req_clen); - } else if(data->state.resume_from) { /* This is because "resume" was selected */ @@ -2565,8 +2641,8 @@ static CURLcode http_range(struct Curl_easy *data, data->state.range, total_len - 1, total_len); } else { - /* Range was selected and then we just pass the incoming range and - append total size */ + /* Range was selected and then we pass the incoming range and append + total size */ data->state.aptr.rangeline = curl_maprintf("Content-Range: bytes %s/%" FMT_OFF_T "\r\n", data->state.range, req_clen); @@ -2894,7 +2970,7 @@ static CURLcode http_add_hd(struct Curl_easy *data, result = Curl_http2_request_upgrade(req, data); } #ifndef CURL_DISABLE_WEBSOCKETS - if(!result && conn->handler->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) + if(!result && conn->scheme->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) result = Curl_ws_request(data, req); #endif break; @@ -3024,7 +3100,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) data->req.upload_chunky = FALSE; out: - if(CURLE_TOO_LARGE == result) + if(result == CURLE_TOO_LARGE) failf(data, "HTTP request too large"); /* clear userpwd and proxyuserpwd to avoid reusing old credentials @@ -3095,7 +3171,7 @@ static statusline checkprotoprefix(struct Curl_easy *data, const char *s, size_t len) { #ifndef CURL_DISABLE_RTSP - if(conn->handler->protocol & CURLPROTO_RTSP) + if(conn->scheme->protocol & CURLPROTO_RTSP) return checkrtspprefix(data, s, len); #else (void)conn; @@ -3106,11 +3182,11 @@ static statusline checkprotoprefix(struct Curl_easy *data, /* HTTP header has field name `n` (a string constant) */ #define HD_IS(hd, hdlen, n) \ - (((hdlen) >= (sizeof(n) - 1)) && curl_strnequal((n), (hd), (sizeof(n) - 1))) + (((hdlen) >= (sizeof(n) - 1)) && curl_strnequal(n, hd, sizeof(n) - 1)) #define HD_VAL(hd, hdlen, n) \ - ((((hdlen) >= (sizeof(n) - 1)) && \ - curl_strnequal((n), (hd), (sizeof(n) - 1)))? (hd + (sizeof(n) - 1)) : NULL) + ((((hdlen) >= (sizeof(n) - 1)) && (hd) && \ + curl_strnequal(n, hd, sizeof(n) - 1)) ? ((hd) + (sizeof(n) - 1)) : NULL) /* HTTP header has field name `n` (a string constant) and contains `v` * (a string constant) in its value(s) */ @@ -3202,7 +3278,7 @@ static CURLcode http_header_c(struct Curl_easy *data, return CURLE_OK; } } - /* negative, different value or just rubbish - bad HTTP */ + /* negative, different value or rubbish - bad HTTP */ failf(data, "Invalid Content-Length: value"); return CURLE_WEIRD_SERVER_REPLY; } @@ -3212,11 +3288,10 @@ static CURLcode http_header_c(struct Curl_easy *data, HD_VAL(hd, hdlen, "Content-Encoding:") : NULL; if(v) { /* - * Process Content-Encoding. Look for the values: identity, - * gzip, deflate, compress, x-gzip and x-compress. x-gzip and - * x-compress are the same as gzip and compress. (Sec 3.5 RFC - * 2616). zlib cannot handle compress. However, errors are - * handled further down when the response body is processed + * Process Content-Encoding. Look for the values: identity, gzip, deflate, + * compress, x-gzip and x-compress. x-gzip and x-compress are the same as + * gzip and compress. (Sec 3.5 RFC 2616). zlib cannot handle compress. + * Errors are handled further down when the response body is processed */ return Curl_build_unencoding_stack(data, v, FALSE); } @@ -3624,7 +3699,7 @@ static CURLcode http_header(struct Curl_easy *data, if(!result) { struct connectdata *conn = data->conn; - if(conn->handler->protocol & CURLPROTO_RTSP) + if(conn->scheme->protocol & CURLPROTO_RTSP) result = Curl_rtsp_parseheader(data, hd); } return result; @@ -3664,12 +3739,12 @@ static CURLcode http_statusline(struct Curl_easy *data, data->info.httpcode = k->httpcode; data->info.httpversion = k->httpversion; - conn->httpversion_seen = (unsigned char)k->httpversion; + conn->httpversion_seen = k->httpversion; if(!data->state.http_neg.rcvd_min || data->state.http_neg.rcvd_min > k->httpversion) /* store the lowest server version we encounter */ - data->state.http_neg.rcvd_min = (unsigned char)k->httpversion; + data->state.http_neg.rcvd_min = k->httpversion; /* * This code executes as part of processing the header. As a @@ -3681,8 +3756,8 @@ static CURLcode http_statusline(struct Curl_easy *data, */ if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && k->httpcode == 416) { - /* "Requested Range Not Satisfiable", just proceed and - pretend this is no error */ + /* "Requested Range Not Satisfiable", proceed and pretend this is no + error */ k->ignorebody = TRUE; /* Avoid appending error msg to good data. */ } @@ -3700,7 +3775,7 @@ static CURLcode http_statusline(struct Curl_easy *data, /* (quote from RFC2616, section 10.3.5): The 304 response * MUST NOT contain a message-body, and thus is always * terminated by the first empty line after the header - * fields. */ + * fields. */ if(data->set.timecondition) data->info.timecond = TRUE; FALLTHROUGH(); @@ -3750,7 +3825,7 @@ static CURLcode verify_header(struct Curl_easy *data, const char *hd, size_t hdlen) { struct SingleRequest *k = &data->req; - char *ptr = memchr(hd, 0x00, hdlen); + const char *ptr = memchr(hd, 0x00, hdlen); if(ptr) { /* this is bad, bail out */ failf(data, "Nul byte in header"); @@ -3800,33 +3875,6 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data, return CURLE_OK; } -static CURLcode http_write_header(struct Curl_easy *data, - const char *hd, size_t hdlen) -{ - CURLcode result; - int writetype; - - /* now, only output this if the header AND body are requested: - */ - Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen); - - writetype = CLIENTWRITE_HEADER | - ((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0); - - result = Curl_client_write(data, writetype, hd, hdlen); - if(result) - return result; - - result = Curl_bump_headersize(data, hdlen, FALSE); - if(result) - return result; - - data->req.deductheadercount = (100 <= data->req.httpcode && - 199 >= data->req.httpcode) ? - data->req.headerbytecount : 0; - return result; -} - static CURLcode http_on_response(struct Curl_easy *data, const char *last_hd, size_t last_hd_len, const char *buf, size_t blen, @@ -3943,7 +3991,7 @@ static CURLcode http_on_response(struct Curl_easy *data, if((k->size == -1) && !k->chunk && !conn->bits.close && (k->httpversion == 11) && - !(conn->handler->protocol & CURLPROTO_RTSP) && + !(conn->scheme->protocol & CURLPROTO_RTSP) && data->state.httpreq != HTTPREQ_HEAD) { /* On HTTP 1.1, when connection is not to get closed, but no Content-Length nor Transfer-Encoding chunked have been @@ -4010,13 +4058,13 @@ static CURLcode http_on_response(struct Curl_easy *data, goto out; if(k->httpcode >= 300) { - if((!data->req.authneg) && !conn->bits.close && + if(!data->req.authneg && !conn->bits.close && !Curl_creader_will_rewind(data)) { /* * General treatment of errors when about to send data. Including : * "417 Expectation Failed", while waiting for 100-continue. * - * The check for close above is done simply because of something + * The check for close above is done because of something * else has already deemed the connection to get closed then * something else should have considered the big picture and we * avoid this check. @@ -4162,7 +4210,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, bool fine_statusline = FALSE; k->httpversion = 0; /* Do not know yet */ - if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) { + if(data->conn->scheme->protocol & PROTO_FAMILY_HTTP) { /* * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 * @@ -4183,7 +4231,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, k->httpversion = (unsigned char)(10 + (p[1] - '0')); p += 3; if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + k->httpcode = ((p[0] - '0') * 100) + ((p[1] - '0') * 10) + (p[2] - '0'); /* RFC 9112 requires a single space following the status code, but the browsers do not so let's not insist */ @@ -4203,7 +4251,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, k->httpversion = (unsigned char)((*p - '0') * 10); p += 2; if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + k->httpcode = ((p[0] - '0') * 100) + ((p[1] - '0') * 10) + (p[2] - '0'); p += 3; if(!ISBLANK(*p)) @@ -4229,7 +4277,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, } } } - else if(data->conn->handler->protocol & CURLPROTO_RTSP) { + else if(data->conn->scheme->protocol & CURLPROTO_RTSP) { const char *p = hd; struct Curl_str ver; curl_off_t status; @@ -4291,12 +4339,12 @@ static CURLcode http_rw_hd(struct Curl_easy *data, void Curl_http_to_fold(struct dynbuf *bf) { size_t len = curlx_dyn_len(bf); - char *hd = curlx_dyn_ptr(bf); + const char *hd = curlx_dyn_ptr(bf); if(len && (hd[len - 1] == '\n')) len--; if(len && (hd[len - 1] == '\r')) len--; - while(len && (ISBLANK(hd[len - 1]))) /* strip off trailing whitespace */ + while(len && ISBLANK(hd[len - 1])) /* strip off trailing whitespace */ len--; curlx_dyn_setlen(bf, len); } @@ -4317,7 +4365,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data, struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - char *end_ptr; + const char *end_ptr; bool leftover_body = FALSE; /* we have bytes for the next header, make sure it is not a folded header @@ -4345,7 +4393,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data, while(blen && k->header) { size_t consumed; size_t hlen; - char *hd; + const char *hd; size_t unfold_len = 0; if(data->state.leading_unfold) { @@ -4607,17 +4655,17 @@ CURLcode Curl_http_req_make(struct httpreq **preq, #pragma GCC diagnostic pop #endif if(scheme) { - req->scheme = Curl_memdup0(scheme, s_len); + req->scheme = curlx_memdup0(scheme, s_len); if(!req->scheme) goto out; } if(authority) { - req->authority = Curl_memdup0(authority, a_len); + req->authority = curlx_memdup0(authority, a_len); if(!req->authority) goto out; } if(path) { - req->path = Curl_memdup0(path, p_len); + req->path = curlx_memdup0(path, p_len); if(!req->path) goto out; } @@ -4931,143 +4979,61 @@ void Curl_http_resp_free(struct http_resp *resp) } } -struct cr_exp100_ctx { - struct Curl_creader super; - struct curltime start; /* time started waiting */ - enum expect100 state; +/* + * HTTP handler interface. + */ +static const struct Curl_protocol Curl_protocol_http = { + Curl_http_setup_conn, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + Curl_http_doing_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + Curl_http_perform_pollset, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + Curl_http_write_resp, /* write_resp */ + Curl_http_write_resp_hd, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ }; -/* Expect: 100-continue client reader, blocking uploads */ - -static void http_exp100_continue(struct Curl_easy *data, - struct Curl_creader *reader) -{ - struct cr_exp100_ctx *ctx = reader->ctx; - if(ctx->state > EXP100_SEND_DATA) { - ctx->state = EXP100_SEND_DATA; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - } -} - -static CURLcode cr_exp100_read(struct Curl_easy *data, - struct Curl_creader *reader, - char *buf, size_t blen, - size_t *nread, bool *eos) -{ - struct cr_exp100_ctx *ctx = reader->ctx; - timediff_t ms; - - switch(ctx->state) { - case EXP100_SENDING_REQUEST: - if(!Curl_req_sendbuf_empty(data)) { - /* The initial request data has not been fully sent yet. Do - * not start the timer yet. */ - DEBUGF(infof(data, "cr_exp100_read, request not full sent yet")); - *nread = 0; - *eos = FALSE; - return CURLE_OK; - } - /* We are now waiting for a reply from the server or - * a timeout on our side IFF the request has been fully sent. */ - DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, " - "timeout %dms", data->set.expect_100_timeout)); - ctx->state = EXP100_AWAITING_CONTINUE; - ctx->start = *Curl_pgrs_now(data); - Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); - *nread = 0; - *eos = FALSE; - return CURLE_OK; - case EXP100_FAILED: - DEBUGF(infof(data, "cr_exp100_read, expectation failed, error")); - *nread = 0; - *eos = FALSE; - return CURLE_READ_ERROR; - case EXP100_AWAITING_CONTINUE: - ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start); - if(ms < data->set.expect_100_timeout) { - DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); - *nread = 0; - *eos = FALSE; - return CURLE_OK; - } - /* we have waited long enough, continue anyway */ - http_exp100_continue(data, reader); - infof(data, "Done waiting for 100-continue"); - FALLTHROUGH(); - default: - DEBUGF(infof(data, "cr_exp100_read, pass through")); - return Curl_creader_read(data, reader->next, buf, blen, nread, eos); - } -} - -static void cr_exp100_done(struct Curl_easy *data, - struct Curl_creader *reader, int premature) -{ - struct cr_exp100_ctx *ctx = reader->ctx; - ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); -} +#endif /* CURL_DISABLE_HTTP */ -static const struct Curl_crtype cr_exp100 = { - "cr-exp100", - Curl_creader_def_init, - cr_exp100_read, - Curl_creader_def_close, - Curl_creader_def_needs_rewind, - Curl_creader_def_total_length, - Curl_creader_def_resume_from, - Curl_creader_def_cntrl, - Curl_creader_def_is_paused, - cr_exp100_done, - sizeof(struct cr_exp100_ctx) +/* + * HTTP handler interface. + */ +const struct Curl_scheme Curl_scheme_http = { + "http", /* scheme */ +#ifdef CURL_DISABLE_HTTP + ZERO_NULL, +#else + &Curl_protocol_http, +#endif + CURLPROTO_HTTP, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PORT_HTTP, /* defport */ }; -static CURLcode http_exp100_add_reader(struct Curl_easy *data) -{ - struct Curl_creader *reader = NULL; - CURLcode result; - - result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL); - if(!result) - result = Curl_creader_add(data, reader); - if(!result) { - struct cr_exp100_ctx *ctx = reader->ctx; - ctx->state = EXP100_SENDING_REQUEST; - } - - if(result && reader) - Curl_creader_free(data, reader); - return result; -} - -static void http_exp100_got100(struct Curl_easy *data) -{ - struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - if(r) - http_exp100_continue(data, r); -} - -static bool http_exp100_is_waiting(struct Curl_easy *data) -{ - struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - if(r) { - struct cr_exp100_ctx *ctx = r->ctx; - return ctx->state == EXP100_AWAITING_CONTINUE; - } - return FALSE; -} - -static void http_exp100_send_anyway(struct Curl_easy *data) -{ - struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - if(r) - http_exp100_continue(data, r); -} - -static bool http_exp100_is_selected(struct Curl_easy *data) -{ - struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - return !!r; -} - -#endif /* CURL_DISABLE_HTTP */ +/* + * HTTPS handler interface. + */ +const struct Curl_scheme Curl_scheme_https = { + "https", /* scheme */ +#if defined(CURL_DISABLE_HTTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_http, +#endif + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PORT_HTTPS, /* defport */ +}; diff --git a/vendor/curl/lib/http.h b/vendor/curl/lib/http.h index 89810b1..bb53595 100644 --- a/vendor/curl/lib/http.h +++ b/vendor/curl/lib/http.h @@ -39,8 +39,8 @@ typedef enum { /* When redirecting transfers. */ typedef enum { - FOLLOW_NONE, /* not used within the function, just a placeholder to - allow initing to this */ + FOLLOW_NONE, /* not used within the function, a placeholder to allow + initing to this */ FOLLOW_FAKE, /* only records stuff, not actually following */ FOLLOW_RETRY, /* set if this is a request retry as opposed to a real redirect following */ @@ -53,17 +53,10 @@ typedef enum { /* bitmask of CURL_HTTP_V* values */ typedef unsigned char http_majors; -#ifndef CURL_DISABLE_HTTP - -#ifdef USE_HTTP3 -#include -#endif +extern const struct Curl_scheme Curl_scheme_http; +extern const struct Curl_scheme Curl_scheme_https; -extern const struct Curl_handler Curl_handler_http; - -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_https; -#endif +#ifndef CURL_DISABLE_HTTP struct dynhds; @@ -107,13 +100,14 @@ CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect, void Curl_http_to_fold(struct dynbuf *bf); void Curl_http_method(struct Curl_easy *data, - const char **method, Curl_HttpReq *); + const char **method, Curl_HttpReq *reqp); /* protocol-specific functions set up to be called by the main engine */ CURLcode Curl_http_setup_conn(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_http(struct Curl_easy *data, bool *done); -CURLcode Curl_http_done(struct Curl_easy *data, CURLcode, bool premature); +CURLcode Curl_http_done(struct Curl_easy *data, + CURLcode status, bool premature); CURLcode Curl_http_doing_pollset(struct Curl_easy *data, struct easy_pollset *ps); CURLcode Curl_http_perform_pollset(struct Curl_easy *data, diff --git a/vendor/curl/lib/http1.c b/vendor/curl/lib/http1.c index 9e58424..60ad32c 100644 --- a/vendor/curl/lib/http1.c +++ b/vendor/curl/lib/http1.c @@ -269,6 +269,11 @@ CURLcode Curl_h1_req_parse_read(struct h1_req_parser *parser, size_t nread; *pnread = 0; + + DEBUGASSERT(buf); + if(!buf) + return CURLE_BAD_FUNCTION_ARGUMENT; + while(!parser->done) { result = next_line(parser, buf, buflen, options, &nread); if(result) { diff --git a/vendor/curl/lib/http1.h b/vendor/curl/lib/http1.h index 124d32e..c289461 100644 --- a/vendor/curl/lib/http1.h +++ b/vendor/curl/lib/http1.h @@ -29,7 +29,7 @@ #include "bufq.h" #include "http.h" -#define H1_PARSE_OPT_NONE (0) +#define H1_PARSE_OPT_NONE 0 #define H1_PARSE_OPT_STRICT (1 << 0) #define H1_PARSE_DEFAULT_MAX_LINE_LEN DYN_HTTP_REQUEST diff --git a/vendor/curl/lib/http2.c b/vendor/curl/lib/http2.c index a8b8d5c..e1c5798 100644 --- a/vendor/curl/lib/http2.c +++ b/vendor/curl/lib/http2.c @@ -24,7 +24,6 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2) -#include #include #include "urldata.h" @@ -52,10 +51,6 @@ #error too old nghttp2 version, upgrade! #endif -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define nghttp2_session_callbacks_set_error_callback(x, y) -#endif - #if (NGHTTP2_VERSION_NUM >= 0x010c00) #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 #endif @@ -68,9 +63,9 @@ #define H2_CONN_WINDOW_SIZE (10 * 1024 * 1024) /* on receiving from TLS, we prep for holding a full stream window */ #define H2_NW_RECV_CHUNKS (H2_CONN_WINDOW_SIZE / H2_CHUNK_SIZE) -/* on send into TLS, we just want to accumulate small frames */ +/* on send into TLS, we want to accumulate small frames */ #define H2_NW_SEND_CHUNKS 1 -/* this is how much we want "in flight" for a stream, unthrottled */ +/* this is how much we want "in flight" for a stream, unthrottled */ #define H2_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) /* this is how much we want "in flight" for a stream, initially, IFF * nghttp2 allows us to tweak the local window size. */ @@ -124,7 +119,60 @@ struct cf_h2_ctx { #undef CF_CTX_CALL_DATA #define CF_CTX_CALL_DATA(cf) ((struct cf_h2_ctx *)(cf)->ctx)->call_data -static void h2_stream_hash_free(unsigned int id, void *stream); +/** + * All about the H2 internals of a stream + */ +struct h2_stream_ctx { + struct bufq sendbuf; /* request buffer */ + struct h1_req_parser h1; /* parsing the request */ + struct dynhds resp_trailers; /* response trailer fields */ + size_t resp_hds_len; /* amount of response header bytes in recvbuf */ + curl_off_t nrcvd_data; /* number of DATA bytes received */ + + char **push_headers; /* allocated array */ + size_t push_headers_used; /* number of entries filled in */ + size_t push_headers_alloc; /* number of entries allocated */ + + int status_code; /* HTTP response status code */ + uint32_t error; /* stream error code */ + CURLcode xfer_result; /* Result of writing out response */ + int32_t local_window_size; /* the local recv window size */ + int32_t id; /* HTTP/2 protocol identifier for stream */ + BIT(resp_hds_complete); /* we have a complete, final response */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(reset_by_server); /* TRUE on stream reset by server */ + BIT(close_handled); /* TRUE if stream closure is handled by libcurl */ + BIT(bodystarted); + BIT(body_eos); /* the complete body has been added to `sendbuf` and + * is being/has been processed from there. */ + BIT(write_paused); /* stream write is paused */ +}; + +static void free_push_headers(struct h2_stream_ctx *stream) +{ + size_t i; + for(i = 0; i < stream->push_headers_used; i++) + curlx_free(stream->push_headers[i]); + Curl_safefree(stream->push_headers); + stream->push_headers_used = 0; +} + +static void h2_stream_ctx_free(struct h2_stream_ctx *stream) +{ + Curl_bufq_free(&stream->sendbuf); + Curl_h1_req_parse_free(&stream->h1); + Curl_dynhds_free(&stream->resp_trailers); + free_push_headers(stream); + curlx_free(stream); +} + +static void h2_stream_hash_free(unsigned int id, void *stream) +{ + (void)id; + DEBUGASSERT(stream); + h2_stream_ctx_free((struct h2_stream_ctx *)stream); +} static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade) { @@ -164,9 +212,9 @@ static uint32_t cf_h2_initial_win_size(struct Curl_easy *data) /* If the transfer has a rate-limit lower than the default initial * stream window size, use that. It needs to be at least 8k or servers * may be unhappy. */ - if(data->progress.dl.rlimit.rate_per_step && - (data->progress.dl.rlimit.rate_per_step < H2_STREAM_WINDOW_SIZE_INITIAL)) - return CURLMAX((uint32_t)data->progress.dl.rlimit.rate_per_step, 8192); + curl_off_t rps = Curl_rlimit_per_step(&data->progress.dl.rlimit); + if((rps > 0) && (rps < H2_STREAM_WINDOW_SIZE_INITIAL)) + return CURLMAX((uint32_t)rps, 8192); #endif return H2_STREAM_WINDOW_SIZE_INITIAL; } @@ -212,44 +260,9 @@ static CURLcode cf_h2_update_settings(struct cf_h2_ctx *ctx, return CURLE_OK; } -static CURLcode nw_out_flush(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static CURLcode h2_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data); - -/** - * All about the H2 internals of a stream - */ -struct h2_stream_ctx { - struct bufq sendbuf; /* request buffer */ - struct h1_req_parser h1; /* parsing the request */ - struct dynhds resp_trailers; /* response trailer fields */ - size_t resp_hds_len; /* amount of response header bytes in recvbuf */ - curl_off_t nrcvd_data; /* number of DATA bytes received */ - - char **push_headers; /* allocated array */ - size_t push_headers_used; /* number of entries filled in */ - size_t push_headers_alloc; /* number of entries allocated */ - - int status_code; /* HTTP response status code */ - uint32_t error; /* stream error code */ - CURLcode xfer_result; /* Result of writing out response */ - int32_t local_window_size; /* the local recv window size */ - int32_t id; /* HTTP/2 protocol identifier for stream */ - BIT(resp_hds_complete); /* we have a complete, final response */ - BIT(closed); /* TRUE on stream close */ - BIT(reset); /* TRUE on stream reset */ - BIT(close_handled); /* TRUE if stream closure is handled by libcurl */ - BIT(bodystarted); - BIT(body_eos); /* the complete body has been added to `sendbuf` and - * is being/has been processed from there. */ - BIT(write_paused); /* stream write is paused */ -}; - -#define H2_STREAM_CTX(ctx, data) \ - ((struct h2_stream_ctx *)( \ - data? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL)) +#define H2_STREAM_CTX(ctx, data) \ + ((struct h2_stream_ctx *)( \ + (data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL)) static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) { @@ -275,31 +288,6 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) return stream; } -static void free_push_headers(struct h2_stream_ctx *stream) -{ - size_t i; - for(i = 0; i < stream->push_headers_used; i++) - curlx_free(stream->push_headers[i]); - Curl_safefree(stream->push_headers); - stream->push_headers_used = 0; -} - -static void h2_stream_ctx_free(struct h2_stream_ctx *stream) -{ - Curl_bufq_free(&stream->sendbuf); - Curl_h1_req_parse_free(&stream->h1); - Curl_dynhds_free(&stream->resp_trailers); - free_push_headers(stream); - curlx_free(stream); -} - -static void h2_stream_hash_free(unsigned int id, void *stream) -{ - (void)id; - DEBUGASSERT(stream); - h2_stream_ctx_free((struct h2_stream_ctx *)stream); -} - #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -406,6 +394,29 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, return CURLE_OK; } +static CURLcode nw_out_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + size_t nwritten; + CURLcode result; + + if(Curl_bufq_is_empty(&ctx->outbufq)) + return CURLE_OK; + + result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0, + &nwritten); + if(result) { + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->outbufq)); + ctx->nw_out_blocked = 1; + } + return result; + } + return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN; +} + static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; @@ -464,154 +475,6 @@ static int h2_client_new(struct Curl_cfilter *cf, return rc; } -static ssize_t send_callback(nghttp2_session *h2, - const uint8_t *mem, size_t length, int flags, - void *userp); -static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, - void *userp); -static int cf_h2_on_invalid_frame_recv(nghttp2_session *session, - const nghttp2_frame *frame, - int lib_error_code, - void *user_data); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, - void *userp); -#endif -static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *mem, size_t len, void *userp); -static int on_stream_close(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *userp); -static int on_begin_headers(nghttp2_session *session, - const nghttp2_frame *frame, void *userp); -static int on_header(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int error_callback(nghttp2_session *session, const char *msg, - size_t len, void *userp); -#endif -static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - struct h2_stream_ctx *stream; - CURLcode result = CURLE_OUT_OF_MEMORY; - int rc; - nghttp2_session_callbacks *cbs = NULL; - - DEBUGASSERT(!ctx->h2); - DEBUGASSERT(ctx->initialized); - - rc = nghttp2_session_callbacks_new(&cbs); - if(rc) { - failf(data, "Could not initialize nghttp2 callbacks"); - goto out; - } - - nghttp2_session_callbacks_set_send_callback(cbs, send_callback); - nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); - nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs, - cf_h2_on_invalid_frame_recv); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send); -#endif - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - cbs, on_data_chunk_recv); - nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); - nghttp2_session_callbacks_set_on_begin_headers_callback( - cbs, on_begin_headers); - nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - nghttp2_session_callbacks_set_error_callback(cbs, error_callback); -#endif - - /* The nghttp2 session is not yet setup, do it */ - rc = h2_client_new(cf, cbs); - if(rc) { - failf(data, "Could not initialize nghttp2"); - goto out; - } - ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; - - if(ctx->via_h1_upgrade) { - /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted - * in the H1 request and we upgrade from there. This stream - * is opened implicitly as #1. */ - uint8_t binsettings[H2_BINSETTINGS_LEN]; - ssize_t rclen; - size_t binlen; /* length of the binsettings data */ - - rclen = populate_binsettings(binsettings, data); - - if(!curlx_sztouz(rclen, &binlen) || !binlen) { - failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); - result = CURLE_FAILED_INIT; - goto out; - } - - result = http2_data_setup(cf, data, &stream); - if(result) - goto out; - DEBUGASSERT(stream); - stream->id = 1; - /* queue SETTINGS frame (again) */ - rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen, - data->state.httpreq == HTTPREQ_HEAD, - NULL); - if(rc) { - failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - - rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id, - data); - if(rc) { - infof(data, "http/2: failed to set user_data for stream %u", - stream->id); - DEBUGASSERT(0); - } - CURL_TRC_CF(data, cf, "created session via Upgrade"); - } - else { - nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; - size_t ivlen; - - ivlen = populate_settings(iv, data, ctx); - rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, - iv, ivlen); - if(rc) { - failf(data, "nghttp2_submit_settings() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - } - - rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, - HTTP2_HUGE_WINDOW_SIZE); - if(rc) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - - /* all set, traffic will be send on connect */ - result = CURLE_OK; - CURL_TRC_CF(data, cf, "[0] created h2 session%s", - ctx->via_h1_upgrade ? " (via h1 upgrade)" : ""); - -out: - if(cbs) - nghttp2_session_callbacks_del(cbs); - return result; -} - /* * Returns nonzero if current HTTP/2 session should be closed. */ @@ -662,8 +525,8 @@ static CURLcode h2_process_pending_input(struct Curl_cfilter *cf, } /* - * 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. + * The server may send us data at any point (e.g. PING frames). Therefore, we + * cannot assume that an HTTP/2 socket is dead because it is readable. * * Check the lower filters first and, if successful, peek at the socket * and distinguish between closed and data. @@ -738,30 +601,6 @@ void Curl_http2_ver(char *p, size_t len) (void)curl_msnprintf(p, len, "nghttp2/%s", h2->version_str); } -static CURLcode nw_out_flush(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - size_t nwritten; - CURLcode result; - - (void)data; - if(Curl_bufq_is_empty(&ctx->outbufq)) - return CURLE_OK; - - result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0, - &nwritten); - if(result) { - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", - Curl_bufq_len(&ctx->outbufq)); - ctx->nw_out_blocked = 1; - } - return result; - } - return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN; -} - /* * The implementation of nghttp2_send_callback type. Here we write |data| with * size |length| to the network and return the number of bytes actually @@ -831,28 +670,28 @@ char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) /* * push header access function. Only to be used from within the push callback */ -char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) +char *curl_pushheader_byname(struct curl_pushheaders *h, const char *name) { struct h2_stream_ctx *stream; size_t len; size_t i; /* Verify that we got a good easy handle in the push header struct, mostly to detect rubbish input fast(er). Also empty header name - is just a rubbish too. We have to allow ":" at the beginning of + is rubbish too. We have to allow ":" at the beginning of the header, but header == ":" must be rejected. If we have ':' in the middle of header, it could be matched in middle of the value, this is because we do prefix match.*/ - if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] || - !strcmp(header, ":") || strchr(header + 1, ':')) + if(!h || !GOOD_EASY_HANDLE(h->data) || !name || !name[0] || + !strcmp(name, ":") || strchr(name + 1, ':')) return NULL; stream = h->stream; if(!stream) return NULL; - len = strlen(header); + len = strlen(name); for(i = 0; i < stream->push_headers_used; i++) { - if(!strncmp(header, stream->push_headers[i], len)) { + if(!strncmp(name, stream->push_headers[i], len)) { /* sub-match, make sure that it is followed by a colon */ if(stream->push_headers[i][len] != ':') continue; @@ -1049,7 +888,6 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf, struct h2_stream_ctx *stream, const char *buf, size_t blen, bool eos) { - /* If we already encountered an error, skip further writes */ if(!stream->xfer_result) { stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); @@ -1066,7 +904,6 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf, struct h2_stream_ctx *stream, const char *buf, size_t blen, bool eos) { - /* If we already encountered an error, skip further writes */ if(!stream->xfer_result) stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); @@ -1143,7 +980,8 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, else stream->status_code = -1; - h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); + h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), + (bool)stream->closed); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -1167,10 +1005,8 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, } break; case NGHTTP2_RST_STREAM: - stream->closed = TRUE; - if(frame->rst_stream.error_code) { - stream->reset = TRUE; - } + if(frame->rst_stream.error_code) + stream->reset_by_server = TRUE; Curl_multi_mark_dirty(data); break; case NGHTTP2_WINDOW_UPDATE: @@ -1207,8 +1043,9 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, return CURLE_OK; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) +#ifdef CURLVERBOSE +int Curl_nghttp2_fr_print(const nghttp2_frame *frame, char *buffer, + size_t blen) { switch(frame->hd.type) { case NGHTTP2_DATA: { @@ -1243,18 +1080,16 @@ static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) return curl_msnprintf(buffer, blen, "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); } - case NGHTTP2_PUSH_PROMISE: { + case NGHTTP2_PUSH_PROMISE: return curl_msnprintf(buffer, blen, "FRAME[PUSH_PROMISE, len=%d, hend=%d]", (int)frame->hd.length, !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); - } - case NGHTTP2_PING: { + case NGHTTP2_PING: return curl_msnprintf(buffer, blen, "FRAME[PING, len=%d, ack=%d]", (int)frame->hd.length, frame->hd.flags & NGHTTP2_FLAG_ACK); - } case NGHTTP2_GOAWAY: { char scratch[128]; size_t s_len = CURL_ARRAYSIZE(scratch); @@ -1289,10 +1124,10 @@ static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, (void)session; DEBUGASSERT(data); - if(data && Curl_trc_cf_is_verbose(cf, data)) { + if(Curl_trc_cf_is_verbose(cf, data)) { char buffer[256]; int len; - len = fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_fr_print(frame, buffer, sizeof(buffer) - 1); buffer[len] = 0; CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); } @@ -1305,7 +1140,7 @@ static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, } return 0; } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp) @@ -1316,15 +1151,15 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, int32_t stream_id = frame->hd.stream_id; DEBUGASSERT(data); -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_cf_is_verbose(cf, data)) { char buffer[256]; int len; - len = fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_fr_print(frame, buffer, sizeof(buffer) - 1); buffer[len] = 0; CURL_TRC_CF(data, cf, "[%d] <- %s", frame->hd.stream_id, buffer); } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ @@ -1394,14 +1229,14 @@ static int cf_h2_on_invalid_frame_recv(nghttp2_session *session, data = nghttp2_session_get_stream_user_data(session, stream_id); if(data) { struct h2_stream_ctx *stream; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE char buffer[256]; int len; - len = fr_print(frame, buffer, sizeof(buffer) - 1); + len = Curl_nghttp2_fr_print(frame, buffer, sizeof(buffer) - 1); buffer[len] = 0; failf(data, "[HTTP2] [%d] received invalid frame: %s, error %d: %s", stream_id, buffer, ngerr, nghttp2_strerror(ngerr)); -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ stream = H2_STREAM_CTX(ctx, data); if(stream) { nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1488,9 +1323,8 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream->closed = TRUE; stream->error = error_code; - if(stream->error) { + if(stream->error) stream->reset = TRUE; - } if(stream->error) CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", @@ -1779,7 +1613,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session, return (nread == 0) ? NGHTTP2_ERR_DEFERRED : nread; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static int error_callback(nghttp2_session *session, const char *msg, size_t len, @@ -1842,36 +1676,31 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, CURLcode result; *pnlen = 0; - if(stream->error == NGHTTP2_REFUSED_STREAM) { - CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " - "connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ - data->state.refused_stream = TRUE; - return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - } - else if(stream->error != NGHTTP2_NO_ERROR) { - if(stream->resp_hds_complete && data->req.no_body) { - CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " - "not want a body anyway, ignore: %s (err %u)", - stream->id, nghttp2_http2_strerror(stream->error), - stream->error); - stream->close_handled = TRUE; - return CURLE_OK; - } - failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", - stream->id, nghttp2_http2_strerror(stream->error), - stream->error); - return CURLE_HTTP2_STREAM; - } - else if(stream->reset) { - failf(data, "HTTP/2 stream %u was reset", stream->id); - return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2; - } - - if(!stream->bodystarted) { - failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " - " all response header fields, treated as error", - stream->id); + if(stream->reset) { + if(stream->error == NGHTTP2_REFUSED_STREAM) { + infof(data, "HTTP/2 stream %d refused by server, try again on a new " + "connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + data->state.refused_stream = TRUE; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ + } + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " + "not want a body anyway, ignore: %s (err %u)", + stream->id, nghttp2_http2_strerror(stream->error), + stream->error); + stream->close_handled = TRUE; + return CURLE_OK; + } + failf(data, "HTTP/2 stream %" PRIu32 " reset by %s (error 0x%" PRIx32 + " %s)", stream->id, stream->reset_by_server ? "server" : "curl", + stream->error, nghttp2_http2_strerror(stream->error)); + return stream->error ? CURLE_HTTP2_STREAM : + (data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2); + } + else if(!stream->bodystarted) { + failf(data, "HTTP/2 stream %d was closed cleanly, but before getting " + "all response header fields, treated as error", stream->id); return CURLE_HTTP2_STREAM; } @@ -2060,10 +1889,9 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { stream = H2_STREAM_CTX(ctx, data); if(stream && (stream->closed || !data_max_bytes)) { - /* We would like to abort here and stop processing, so that - * the transfer loop can handle the data/close here. However, - * this may leave data in underlying buffers that will not - * be consumed. */ + /* We would like to abort here and stop processing, so that the transfer + * loop can handle the data/close here. This may leave data in + * underlying buffers that will not be consumed. */ if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data)) Curl_multi_mark_dirty(data); break; @@ -2309,7 +2137,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, goto out; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE #define MAX_ACC 60000 /* <64KB to account for some overhead */ if(Curl_trc_is_verbose(data)) { size_t acc = 0, i; @@ -2534,6 +2362,125 @@ static CURLcode cf_h2_adjust_pollset(struct Curl_cfilter *cf, return result; } +static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; + CURLcode result = CURLE_OUT_OF_MEMORY; + int rc; + nghttp2_session_callbacks *cbs = NULL; + + DEBUGASSERT(!ctx->h2); + DEBUGASSERT(ctx->initialized); + + rc = nghttp2_session_callbacks_new(&cbs); + if(rc) { + failf(data, "Could not initialize nghttp2 callbacks"); + goto out; + } + + nghttp2_session_callbacks_set_send_callback(cbs, send_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs, + cf_h2_on_invalid_frame_recv); +#ifdef CURLVERBOSE + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send); +#endif + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + cbs, on_data_chunk_recv); + nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); + nghttp2_session_callbacks_set_on_begin_headers_callback( + cbs, on_begin_headers); + nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); +#ifdef CURLVERBOSE + nghttp2_session_callbacks_set_error_callback(cbs, error_callback); +#endif + + /* The nghttp2 session is not yet setup, do it */ + rc = h2_client_new(cf, cbs); + if(rc) { + failf(data, "Could not initialize nghttp2"); + goto out; + } + ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; + + if(ctx->via_h1_upgrade) { + /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted + * in the H1 request and we upgrade from there. This stream + * is opened implicitly as #1. */ + uint8_t binsettings[H2_BINSETTINGS_LEN]; + ssize_t rclen; + size_t binlen; /* length of the binsettings data */ + + rclen = populate_binsettings(binsettings, data); + + if(!curlx_sztouz(rclen, &binlen) || !binlen) { + failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); + result = CURLE_FAILED_INIT; + goto out; + } + + result = http2_data_setup(cf, data, &stream); + if(result) + goto out; + DEBUGASSERT(stream); + stream->id = 1; + /* queue SETTINGS frame (again) */ + rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen, + data->state.httpreq == HTTPREQ_HEAD, + NULL); + if(rc) { + failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id, + data); + if(rc) { + infof(data, "http/2: failed to set user_data for stream %u", + stream->id); + DEBUGASSERT(0); + } + CURL_TRC_CF(data, cf, "created session via Upgrade"); + } + else { + nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; + size_t ivlen; + + ivlen = populate_settings(iv, data, ctx); + rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, + iv, ivlen); + if(rc) { + failf(data, "nghttp2_submit_settings() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + } + + rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, + HTTP2_HUGE_WINDOW_SIZE); + if(rc) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + /* all set, traffic will be send on connect */ + result = CURLE_OK; + CURL_TRC_CF(data, cf, "[0] created h2 session%s", + ctx->via_h1_upgrade ? " (via h1 upgrade)" : ""); + +out: + if(cbs) + nghttp2_session_callbacks_del(cbs); + return result; +} + static CURLcode cf_h2_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -3051,10 +2998,10 @@ char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) return NULL; } -char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) +char *curl_pushheader_byname(struct curl_pushheaders *h, const char *name) { (void)h; - (void)header; + (void)name; return NULL; } diff --git a/vendor/curl/lib/http2.h b/vendor/curl/lib/http2.h index 950b2f7..a14c848 100644 --- a/vendor/curl/lib/http2.h +++ b/vendor/curl/lib/http2.h @@ -36,6 +36,11 @@ */ void Curl_http2_ver(char *p, size_t len); +#ifdef CURLVERBOSE +int Curl_nghttp2_fr_print(const nghttp2_frame *frame, char *buffer, + size_t blen); +#endif + CURLcode Curl_http2_request_upgrade(struct dynbuf *req, struct Curl_easy *data); @@ -50,7 +55,7 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode Curl_http2_upgrade(struct Curl_easy *data, struct connectdata *conn, int sockindex, - const char *ptr, size_t nread); + const char *mem, size_t nread); void *Curl_nghttp2_malloc(size_t size, void *user_data); void Curl_nghttp2_free(void *ptr, void *user_data); diff --git a/vendor/curl/lib/http_aws_sigv4.c b/vendor/curl/lib/http_aws_sigv4.c index 598d8cb..492be1a 100644 --- a/vendor/curl/lib/http_aws_sigv4.c +++ b/vendor/curl/lib/http_aws_sigv4.c @@ -27,7 +27,7 @@ #include "urldata.h" #include "strcase.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "http_aws_sigv4.h" #include "curl_sha256.h" #include "transfer.h" @@ -38,22 +38,22 @@ #include -#define HMAC_SHA256(k, kl, d, dl, o) \ - do { \ - result = Curl_hmacit(&Curl_HMAC_SHA256, \ - (const unsigned char *)k, \ - kl, \ - (const unsigned char *)d, \ - dl, o); \ - if(result) { \ - goto fail; \ - } \ +#define HMAC_SHA256(k, kl, d, dl, o) \ + do { \ + result = Curl_hmacit(&Curl_HMAC_SHA256, \ + (const unsigned char *)(k), \ + kl, \ + (const unsigned char *)(d), \ + dl, o); \ + if(result) { \ + goto fail; \ + } \ } while(0) #define TIMESTAMP_SIZE 17 /* hex-encoded with trailing null */ -#define SHA256_HEX_LENGTH (2 * CURL_SHA256_DIGEST_LENGTH + 1) +#define SHA256_HEX_LENGTH ((2 * CURL_SHA256_DIGEST_LENGTH) + 1) #define MAX_QUERY_COMPONENTS 128 @@ -62,20 +62,6 @@ struct pair { struct dynbuf value; }; -static void dyn_array_free(struct dynbuf *db, size_t num_elements); -static void pair_array_free(struct pair *pair_array, size_t num_elements); -static CURLcode split_to_dyn_array(const char *source, - struct dynbuf db[MAX_QUERY_COMPONENTS], - size_t *num_splits); -static bool is_reserved_char(const char c); -static CURLcode uri_encode_path(struct Curl_str *original_path, - struct dynbuf *new_path); -static CURLcode encode_query_component(char *component, size_t len, - struct dynbuf *db); -static CURLcode http_aws_decode_encode(const char *in, size_t in_len, - struct dynbuf *out); -static bool should_urlencode(struct Curl_str *service_name); - static void sha256_to_hex(char *dst, unsigned char *sha) { Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH, @@ -129,6 +115,171 @@ static void trim_headers(struct curl_slist *head) } } +/* + * Frees all allocated strings in a dynbuf pair array, and the dynbuf itself + */ +static void pair_array_free(struct pair *pair_array, size_t num_elements) +{ + size_t index; + + for(index = 0; index != num_elements; index++) { + curlx_dyn_free(&pair_array[index].key); + curlx_dyn_free(&pair_array[index].value); + } +} + +/* + * Frees all allocated strings in a split dynbuf, and the dynbuf itself + */ +static void dyn_array_free(struct dynbuf *db, size_t num_elements) +{ + size_t index; + + for(index = 0; index < num_elements; index++) + curlx_dyn_free((&db[index])); +} + +/* + * Splits source string by SPLIT_BY, and creates an array of dynbuf in db. + * db is initialized by this function. + * Caller is responsible for freeing the array elements with dyn_array_free + */ + +#define SPLIT_BY '&' + +static CURLcode split_to_dyn_array(const char *source, + struct dynbuf db[MAX_QUERY_COMPONENTS], + size_t *num_splits_out) +{ + CURLcode result = CURLE_OK; + size_t len = strlen(source); + size_t pos; /* Position in result buffer */ + size_t start = 0; /* Start of current segment */ + size_t segment_length = 0; + size_t index = 0; + size_t num_splits = 0; + + /* Split source_ptr on SPLIT_BY and store the segment offsets and length in + * array */ + for(pos = 0; pos < len; pos++) { + if(source[pos] == SPLIT_BY) { + if(segment_length) { + curlx_dyn_init(&db[index], segment_length + 1); + result = curlx_dyn_addn(&db[index], &source[start], segment_length); + if(result) + goto fail; + + segment_length = 0; + index++; + if(++num_splits == MAX_QUERY_COMPONENTS) { + result = CURLE_TOO_LARGE; + goto fail; + } + } + start = pos + 1; + } + else { + segment_length++; + } + } + + if(segment_length) { + curlx_dyn_init(&db[index], segment_length + 1); + result = curlx_dyn_addn(&db[index], &source[start], segment_length); + if(!result) { + if(++num_splits == MAX_QUERY_COMPONENTS) + result = CURLE_TOO_LARGE; + } + } +fail: + *num_splits_out = num_splits; + return result; +} + +static bool is_reserved_char(const char c) +{ + return (ISALNUM(c) || ISURLPUNTCS(c)); +} + +static CURLcode uri_encode_path(struct Curl_str *original_path, + struct dynbuf *new_path) +{ + const char *p = curlx_str(original_path); + size_t i; + + for(i = 0; i < curlx_strlen(original_path); i++) { + /* Do not encode slashes or unreserved chars from RFC 3986 */ + CURLcode result = CURLE_OK; + unsigned char c = p[i]; + if(is_reserved_char(c) || c == '/') + result = curlx_dyn_addn(new_path, &c, 1); + else + result = curlx_dyn_addf(new_path, "%%%02X", c); + if(result) + return result; + } + + return CURLE_OK; +} + +/* Normalize the query part. Make sure %2B is left percent encoded, and not + decoded to plus, then encoded to space. +*/ +static CURLcode normalize_query(const char *string, size_t len, + struct dynbuf *db) +{ + CURLcode result = CURLE_OK; + + while(len && !result) { + unsigned char in = (unsigned char)*string; + if(('%' == in) && (len > 2) && + ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { + /* this is two hexadecimal digits following a '%' */ + in = (unsigned char)((curlx_hexval(string[1]) << 4) | + curlx_hexval(string[2])); + string += 3; + len -= 3; + if(in == '+') { + /* decodes to plus, so leave this encoded */ + result = curlx_dyn_addn(db, "%2B", 3); + continue; + } + } + else { + string++; + len--; + } + + if(is_reserved_char(in)) + /* Escape unreserved chars from RFC 3986 */ + result = curlx_dyn_addn(db, &in, 1); + else if(in == '+') + /* Encode '+' as space */ + result = curlx_dyn_add(db, "%20"); + else + result = curlx_dyn_addf(db, "%%%02X", in); + } + + return result; +} + +static bool should_urlencode(struct Curl_str *service_name) +{ + /* + * These services require unmodified (not additionally URL-encoded) URL + * paths. + * should_urlencode == true is equivalent to should_urlencode_uri_path + * from the AWS SDK. Urls are already normalized by the curl URL parser + */ + + if(curlx_str_cmp(service_name, "s3") || + curlx_str_cmp(service_name, "s3-express") || + curlx_str_cmp(service_name, "s3-outposts")) { + return false; + } + return true; +} + /* maximum length for the aws sivg4 parts */ #define MAX_SIGV4_LEN 64 #define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date")) @@ -182,8 +333,8 @@ static CURLcode merge_duplicate_headers(struct curl_slist *head) if(compare_header_names(curr->data, next->data) == 0) { struct dynbuf buf; - char *colon_next; - char *val_next; + const char *colon_next; + const char *val_next; curlx_dyn_init(&buf, CURL_MAX_HTTP_HEADER); @@ -254,7 +405,7 @@ static CURLcode make_headers(struct Curl_easy *data, if(data->state.aptr.host) { /* remove /r/n as the separator for canonical request must be '\n' */ size_t pos = strcspn(data->state.aptr.host, "\n\r"); - fullhost = Curl_memdup0(data->state.aptr.host, pos); + fullhost = curlx_memdup0(data->state.aptr.host, pos); } else fullhost = curl_maprintf("host:%s", hostname); @@ -290,8 +441,9 @@ static CURLcode make_headers(struct Curl_easy *data, semi-colon, are not added to this list. */ for(l = data->set.headers; l; l = l->next) { - char *dupdata, *ptr; - char *sep = strchr(l->data, ':'); + char *dupdata; + const char *ptr; + const char *sep = strchr(l->data, ':'); if(!sep) sep = strchr(l->data, ';'); if(!sep || (*sep == ':' && !*(sep + 1))) @@ -493,7 +645,6 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, static int compare_func(const void *a, const void *b) { - const struct pair *aa = a; const struct pair *bb = b; const size_t aa_key_len = curlx_dyn_len(&aa->key); @@ -578,9 +729,9 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) for(index = 0; index < num_query_components; index++) { const char *in_key; size_t in_key_len; - char *offset; + const char *offset; size_t query_part_len = curlx_dyn_len(&query_array[index]); - char *query_part = curlx_dyn_ptr(&query_array[index]); + const char *query_part = curlx_dyn_ptr(&query_array[index]); in_key = query_part; @@ -593,13 +744,15 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) in_key_len = offset - in_key; } - curlx_dyn_init(&encoded_query_array[index].key, query_part_len * 3 + 1); - curlx_dyn_init(&encoded_query_array[index].value, query_part_len * 3 + 1); + curlx_dyn_init(&encoded_query_array[index].key, + (query_part_len * 3) + 1); + curlx_dyn_init(&encoded_query_array[index].value, + (query_part_len * 3) + 1); counted_query_components++; /* Decode/encode the key */ - result = http_aws_decode_encode(in_key, in_key_len, - &encoded_query_array[index].key); + result = normalize_query(in_key, in_key_len, + &encoded_query_array[index].key); if(result) { goto fail; } @@ -609,8 +762,8 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) size_t in_value_len; const char *in_value = offset + 1; in_value_len = query_part + query_part_len - (offset + 1); - result = http_aws_decode_encode(in_value, in_value_len, - &encoded_query_array[index].value); + result = normalize_query(in_value, in_value_len, + &encoded_query_array[index].value); if(result) { goto fail; } @@ -636,8 +789,8 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) if(index) result = curlx_dyn_addn(dq, "&", 1); if(!result) { - char *key_ptr = curlx_dyn_ptr(&encoded_query_array[index].key); - char *value_ptr = curlx_dyn_ptr(&encoded_query_array[index].value); + const char *key_ptr = curlx_dyn_ptr(&encoded_query_array[index].key); + const char *value_ptr = curlx_dyn_ptr(&encoded_query_array[index].value); size_t vlen = curlx_dyn_len(&encoded_query_array[index].value); if(value_ptr && vlen) { result = curlx_dyn_addf(dq, "%s=%s", key_ptr, value_ptr); @@ -834,8 +987,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) goto fail; result = canon_path(data->state.up.path, strlen(data->state.up.path), - &canonical_path, - should_urlencode(&service)); + &canonical_path, + should_urlencode(&service)); if(result) goto fail; result = CURLE_OUT_OF_MEMORY; @@ -973,172 +1126,4 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) return result; } -/* - * Frees all allocated strings in a dynbuf pair array, and the dynbuf itself - */ - -static void pair_array_free(struct pair *pair_array, size_t num_elements) -{ - size_t index; - - for(index = 0; index != num_elements; index++) { - curlx_dyn_free(&pair_array[index].key); - curlx_dyn_free(&pair_array[index].value); - } -} - -/* - * Frees all allocated strings in a split dynbuf, and the dynbuf itself - */ - -static void dyn_array_free(struct dynbuf *db, size_t num_elements) -{ - size_t index; - - for(index = 0; index < num_elements; index++) - curlx_dyn_free((&db[index])); -} - -/* - * Splits source string by SPLIT_BY, and creates an array of dynbuf in db. - * db is initialized by this function. - * Caller is responsible for freeing the array elements with dyn_array_free - */ - -#define SPLIT_BY '&' - -static CURLcode split_to_dyn_array(const char *source, - struct dynbuf db[MAX_QUERY_COMPONENTS], - size_t *num_splits_out) -{ - CURLcode result = CURLE_OK; - size_t len = strlen(source); - size_t pos; /* Position in result buffer */ - size_t start = 0; /* Start of current segment */ - size_t segment_length = 0; - size_t index = 0; - size_t num_splits = 0; - - /* Split source_ptr on SPLIT_BY and store the segment offsets and length in - * array */ - for(pos = 0; pos < len; pos++) { - if(source[pos] == SPLIT_BY) { - if(segment_length) { - curlx_dyn_init(&db[index], segment_length + 1); - result = curlx_dyn_addn(&db[index], &source[start], segment_length); - if(result) - goto fail; - - segment_length = 0; - index++; - if(++num_splits == MAX_QUERY_COMPONENTS) { - result = CURLE_TOO_LARGE; - goto fail; - } - } - start = pos + 1; - } - else { - segment_length++; - } - } - - if(segment_length) { - curlx_dyn_init(&db[index], segment_length + 1); - result = curlx_dyn_addn(&db[index], &source[start], segment_length); - if(!result) { - if(++num_splits == MAX_QUERY_COMPONENTS) - result = CURLE_TOO_LARGE; - } - } -fail: - *num_splits_out = num_splits; - return result; -} - -static bool is_reserved_char(const char c) -{ - return (ISALNUM(c) || ISURLPUNTCS(c)); -} - -static CURLcode uri_encode_path(struct Curl_str *original_path, - struct dynbuf *new_path) -{ - const char *p = curlx_str(original_path); - size_t i; - - for(i = 0; i < curlx_strlen(original_path); i++) { - /* Do not encode slashes or unreserved chars from RFC 3986 */ - CURLcode result = CURLE_OK; - unsigned char c = p[i]; - if(is_reserved_char(c) || c == '/') - result = curlx_dyn_addn(new_path, &c, 1); - else - result = curlx_dyn_addf(new_path, "%%%02X", c); - if(result) - return result; - } - - return CURLE_OK; -} - -static CURLcode encode_query_component(char *component, size_t len, - struct dynbuf *db) -{ - size_t i; - for(i = 0; i < len; i++) { - CURLcode result = CURLE_OK; - unsigned char this_char = component[i]; - - if(is_reserved_char(this_char)) - /* Escape unreserved chars from RFC 3986 */ - result = curlx_dyn_addn(db, &this_char, 1); - else if(this_char == '+') - /* Encode '+' as space */ - result = curlx_dyn_add(db, "%20"); - else - result = curlx_dyn_addf(db, "%%%02X", this_char); - if(result) - return result; - } - - return CURLE_OK; -} - -/* - * Populates a dynbuf containing url_encode(url_decode(in)) - */ - -static CURLcode http_aws_decode_encode(const char *in, size_t in_len, - struct dynbuf *out) -{ - char *out_s; - size_t out_s_len; - CURLcode result = - Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA); - - if(!result) { - result = encode_query_component(out_s, out_s_len, out); - Curl_safefree(out_s); - } - return result; -} - -static bool should_urlencode(struct Curl_str *service_name) -{ - /* - * These services require unmodified (not additionally URL-encoded) URL - * paths. - * should_urlencode == true is equivalent to should_urlencode_uri_path - * from the AWS SDK. Urls are already normalized by the curl URL parser - */ - - if(curlx_str_cmp(service_name, "s3") || - curlx_str_cmp(service_name, "s3-express") || - curlx_str_cmp(service_name, "s3-outposts")) { - return false; - } - return true; -} - #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_AWS */ diff --git a/vendor/curl/lib/http_chunks.c b/vendor/curl/lib/http_chunks.c index f78a89e..db650b1 100644 --- a/vendor/curl/lib/http_chunks.c +++ b/vendor/curl/lib/http_chunks.c @@ -33,12 +33,12 @@ #include "curlx/strparse.h" /* - * Chunk format (simplified): - * - * [ chunk extension ] CRLF - * CRLF - * - * Highlights from RFC2616 section 3.6 say: + Chunk format (simplified): + + [ chunk extension ] CRLF + CRLF + + Highlights from RFC2616 section 3.6 say: The chunked encoding modifies the body of a message in order to transfer it as a series of chunks, each with its own size indicator, @@ -228,7 +228,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, case CHUNK_POSTLF: if(*buf == 0x0a) { /* The last one before we go back to hex state and start all over. */ - Curl_httpchunk_reset(data, ch, ch->ignore_body); + Curl_httpchunk_reset(data, ch, (bool)ch->ignore_body); } else if(*buf != 0x0d) { ch->state = CHUNK_FAILED; @@ -242,7 +242,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, case CHUNK_TRAILER: if((*buf == 0x0d) || (*buf == 0x0a)) { - char *tr = curlx_dyn_ptr(&ch->trailer); + const char *tr = curlx_dyn_ptr(&ch->trailer); /* this is the end of a trailer, but if the trailer was zero bytes there was no trailer and we move on */ @@ -460,7 +460,7 @@ const struct Curl_cwtype Curl_httpchunk_unencoder = { }; /* max length of an HTTP chunk that we want to generate */ -#define CURL_CHUNKED_MINLEN (1024) +#define CURL_CHUNKED_MINLEN 1024 #define CURL_CHUNKED_MAXLEN (64 * 1024) struct chunked_reader { @@ -517,7 +517,7 @@ static CURLcode add_last_chunk(struct Curl_easy *data, for(tr = trailers; tr; tr = tr->next) { /* only add correctly formatted trailers */ - char *ptr = strchr(tr->data, ':'); + const char *ptr = strchr(tr->data, ':'); if(!ptr || *(ptr + 1) != ' ') { infof(data, "Malformatted trailing header, skipping trailer"); continue; @@ -583,7 +583,7 @@ static CURLcode add_chunk(struct Curl_easy *data, if(!result) result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n); CURL_TRC_READ(data, "http_chunk, made chunk of %zu bytes -> %d", - nread, result); + nread, result); if(result) return result; } @@ -602,7 +602,7 @@ static CURLcode cr_chunked_read(struct Curl_easy *data, CURLcode result = CURLE_READ_ERROR; *pnread = 0; - *peos = ctx->eos; + *peos = (bool)ctx->eos; if(!ctx->eos) { if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) { diff --git a/vendor/curl/lib/http_digest.c b/vendor/curl/lib/http_digest.c index ccc67f5..f5b20c5 100644 --- a/vendor/curl/lib/http_digest.c +++ b/vendor/curl/lib/http_digest.c @@ -69,7 +69,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, { CURLcode result; unsigned char *path = NULL; - char *tmp = NULL; + const char *tmp = NULL; char *response; size_t len; bool have_chlg; diff --git a/vendor/curl/lib/http_negotiate.c b/vendor/curl/lib/http_negotiate.c index be1063d..a6e5c19 100644 --- a/vendor/curl/lib/http_negotiate.c +++ b/vendor/curl/lib/http_negotiate.c @@ -233,19 +233,19 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, } *state = GSS_AUTHSENT; - #ifdef HAVE_GSSAPI +#ifdef HAVE_GSSAPI if(neg_ctx->status == GSS_S_COMPLETE || neg_ctx->status == GSS_S_CONTINUE_NEEDED) { *state = GSS_AUTHDONE; } - #else - #ifdef USE_WINDOWS_SSPI +#else +#ifdef USE_WINDOWS_SSPI if(neg_ctx->status == SEC_E_OK || neg_ctx->status == SEC_I_CONTINUE_NEEDED) { *state = GSS_AUTHDONE; } - #endif - #endif +#endif +#endif } if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) { diff --git a/vendor/curl/lib/http_proxy.c b/vendor/curl/lib/http_proxy.c index c5b2f3e..a9521c9 100644 --- a/vendor/curl/lib/http_proxy.c +++ b/vendor/curl/lib/http_proxy.c @@ -34,7 +34,6 @@ #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" #include "connect.h" -#include "transfer.h" #include "vauth/vauth.h" #include "curlx/strparse.h" @@ -187,7 +186,7 @@ void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, if(*phostname != cf->conn->host.name) *pipv6_ip = (strchr(*phostname, ':') != NULL); else - *pipv6_ip = cf->conn->bits.ipv6_ip; + *pipv6_ip = (bool)cf->conn->bits.ipv6_ip; } struct cf_proxy_ctx { @@ -380,7 +379,6 @@ static void http_proxy_cf_destroy(struct Curl_cfilter *cf, { struct cf_proxy_ctx *ctx = cf->ctx; - (void)data; CURL_TRC_CF(data, cf, "destroy"); curlx_free(ctx); } diff --git a/vendor/curl/lib/httpsrr.c b/vendor/curl/lib/httpsrr.c index d642a4c..169a0b8 100644 --- a/vendor/curl/lib/httpsrr.c +++ b/vendor/curl/lib/httpsrr.c @@ -29,7 +29,7 @@ #include "httpsrr.h" #include "connect.h" #include "curl_trc.h" -#include "strdup.h" +#include "curlx/strdup.h" static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len, unsigned char *alpns) @@ -92,7 +92,7 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data, if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */ return CURLE_BAD_FUNCTION_ARGUMENT; curlx_free(hi->ipv4hints); - hi->ipv4hints = Curl_memdup(val, vlen); + hi->ipv4hints = curlx_memdup(val, vlen); if(!hi->ipv4hints) return CURLE_OUT_OF_MEMORY; hi->ipv4hints_len = vlen; @@ -102,7 +102,7 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data, if(!vlen) return CURLE_BAD_FUNCTION_ARGUMENT; curlx_free(hi->echconfiglist); - hi->echconfiglist = Curl_memdup(val, vlen); + hi->echconfiglist = curlx_memdup(val, vlen); if(!hi->echconfiglist) return CURLE_OUT_OF_MEMORY; hi->echconfiglist_len = vlen; @@ -112,7 +112,7 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data, if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */ return CURLE_BAD_FUNCTION_ARGUMENT; curlx_free(hi->ipv6hints); - hi->ipv6hints = Curl_memdup(val, vlen); + hi->ipv6hints = curlx_memdup(val, vlen); if(!hi->ipv6hints) return CURLE_OUT_OF_MEMORY; hi->ipv6hints_len = vlen; @@ -134,7 +134,7 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data, struct Curl_https_rrinfo * Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo) { - struct Curl_https_rrinfo *dup = Curl_memdup(rrinfo, sizeof(*rrinfo)); + struct Curl_https_rrinfo *dup = curlx_memdup(rrinfo, sizeof(*rrinfo)); if(dup) memset(rrinfo, 0, sizeof(*rrinfo)); return dup; diff --git a/vendor/curl/lib/idn.c b/vendor/curl/lib/idn.c index f055882..2db4f6d 100644 --- a/vendor/curl/lib/idn.c +++ b/vendor/curl/lib/idn.c @@ -37,7 +37,7 @@ idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) #else #define IDN2_LOOKUP(name, host, flags) \ - idn2_lookup_ul((const char *)name, (char **)host, flags) + idn2_lookup_ul((const char *)(name), (char **)(host), flags) #endif #endif /* USE_LIBIDN2 */ @@ -143,20 +143,6 @@ static CURLcode mac_ascii_to_idn(const char *in, char **out) #ifdef USE_WIN32_IDN /* using Windows kernel32 and normaliz libraries. */ -#if (!defined(_WIN32_WINNT) || _WIN32_WINNT < _WIN32_WINNT_VISTA) && \ - (!defined(WINVER) || WINVER < 0x600) -WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags, - const WCHAR *lpUnicodeCharStr, - int cchUnicodeChar, - WCHAR *lpASCIICharStr, - int cchASCIIChar); -WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, - const WCHAR *lpASCIICharStr, - int cchASCIIChar, - WCHAR *lpUnicodeCharStr, - int cchUnicodeChar); -#endif - #define IDN_MAX_LENGTH 255 static char *idn_curlx_convert_wchar_to_UTF8(const wchar_t *str_w, int chars) diff --git a/vendor/curl/lib/idn.h b/vendor/curl/lib/idn.h index 42613af..90d8e81 100644 --- a/vendor/curl/lib/idn.h +++ b/vendor/curl/lib/idn.h @@ -30,7 +30,7 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host); #define USE_IDN void Curl_free_idnconverted_hostname(struct hostname *host); CURLcode Curl_idn_decode(const char *input, char **output); -CURLcode Curl_idn_encode(const char *input, char **output); +CURLcode Curl_idn_encode(const char *puny, char **output); #else #define Curl_free_idnconverted_hostname(x) #define Curl_idn_decode(x) NULL diff --git a/vendor/curl/lib/if2ip.c b/vendor/curl/lib/if2ip.c index d940c87..05e3f84 100644 --- a/vendor/curl/lib/if2ip.c +++ b/vendor/curl/lib/if2ip.c @@ -203,7 +203,7 @@ if2ip_result_t Curl_if2ip(int af, return IF2IP_NOT_FOUND; dummy = CURL_SOCKET(AF_INET, SOCK_STREAM, 0); - if(CURL_SOCKET_BAD == dummy) + if(dummy == CURL_SOCKET_BAD) return IF2IP_NOT_FOUND; memset(&req, 0, sizeof(req)); diff --git a/vendor/curl/lib/imap.c b/vendor/curl/lib/imap.c index 0af1878..0546595 100644 --- a/vendor/curl/lib/imap.c +++ b/vendor/curl/lib/imap.c @@ -35,6 +35,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "imap.h" #ifndef CURL_DISABLE_IMAP @@ -53,7 +55,6 @@ #endif #include "curlx/dynbuf.h" -#include "urldata.h" #include "sendf.h" #include "curl_trc.h" #include "hostip.h" @@ -61,7 +62,6 @@ #include "transfer.h" #include "escape.h" #include "pingpong.h" -#include "imap.h" #include "mime.h" #include "curlx/strparse.h" #include "strcase.h" @@ -137,128 +137,132 @@ struct IMAP { BIT(uidvalidity_set); }; -/* Local API functions */ -static CURLcode imap_regular_transfer(struct Curl_easy *data, - struct IMAP *imap, - bool *done); -static CURLcode imap_do(struct Curl_easy *data, bool *done); -static CURLcode imap_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode imap_connect(struct Curl_easy *data, bool *done); -static CURLcode imap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode imap_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode imap_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static char *imap_atom(const char *str, bool escape_only); +#define IMAP_RESP_OK 1 +#define IMAP_RESP_NOT_OK 2 +#define IMAP_RESP_PREAUTH 3 + +struct ulbits { + int bit; + const char *flag; +}; + +/*********************************************************************** + * + * imap_sendf() + * + * Sends the formatted string as an IMAP command to the server. + * + * Designed to never block. + */ static CURLcode imap_sendf(struct Curl_easy *data, struct imap_conn *imapc, - const char *fmt, ...) CURL_PRINTF(3, 4); -static CURLcode imap_parse_url_options(struct connectdata *conn, - struct imap_conn *imapc); -static CURLcode imap_parse_url_path(struct Curl_easy *data, - struct IMAP *imap); -static CURLcode imap_parse_custom_request(struct Curl_easy *data, - struct IMAP *imap); -static CURLcode imap_perform_authenticate(struct Curl_easy *data, - const char *mech, - const struct bufref *initresp); -static CURLcode imap_continue_authenticate(struct Curl_easy *data, - const char *mech, - const struct bufref *resp); -static CURLcode imap_cancel_authenticate(struct Curl_easy *data, - const char *mech); -static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out); -static void imap_easy_reset(struct IMAP *imap); + const char *fmt, ...) CURL_PRINTF(3, 0); +static CURLcode imap_sendf(struct Curl_easy *data, + struct imap_conn *imapc, + const char *fmt, ...) +{ + CURLcode result = CURLE_OK; -/* - * IMAP protocol handler. - */ + DEBUGASSERT(fmt); -const struct Curl_handler Curl_handler_imap = { - "imap", /* scheme */ - imap_setup_connection, /* setup_connection */ - imap_do, /* do_it */ - imap_done, /* done */ - ZERO_NULL, /* do_more */ - imap_connect, /* connect_it */ - imap_multi_statemach, /* connecting */ - imap_doing, /* doing */ - imap_pollset, /* proto_pollset */ - imap_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - imap_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_IMAP, /* defport */ - CURLPROTO_IMAP, /* protocol */ - CURLPROTO_IMAP, /* family */ - PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | - PROTOPT_CONN_REUSE -}; + /* Calculate the tag based on the connection ID and command ID */ + curl_msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", + 'A' + curlx_sltosi((long)(data->conn->connection_id % 26)), + ++imapc->cmdid); -#ifdef USE_SSL -/* - * IMAPS protocol handler. - */ + /* start with a blank buffer */ + curlx_dyn_reset(&imapc->dyn); -const struct Curl_handler Curl_handler_imaps = { - "imaps", /* scheme */ - imap_setup_connection, /* setup_connection */ - imap_do, /* do_it */ - imap_done, /* done */ - ZERO_NULL, /* do_more */ - imap_connect, /* connect_it */ - imap_multi_statemach, /* connecting */ - imap_doing, /* doing */ - imap_pollset, /* proto_pollset */ - imap_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - imap_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_IMAPS, /* defport */ - CURLPROTO_IMAPS, /* protocol */ - CURLPROTO_IMAP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE -}; + /* append tag + space + fmt */ + result = curlx_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt); + if(!result) { + va_list ap; + va_start(ap, fmt); +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + result = Curl_pp_vsendf(data, &imapc->pp, curlx_dyn_ptr(&imapc->dyn), ap); +#ifdef __clang__ +#pragma clang diagnostic pop #endif + va_end(ap); + } + return result; +} -#define IMAP_RESP_OK 1 -#define IMAP_RESP_NOT_OK 2 -#define IMAP_RESP_PREAUTH 3 +/*********************************************************************** + * + * imap_atom() + * + * Checks the input string for characters that need escaping and returns an + * atom ready for sending to the server. + * + * The returned string needs to be freed. + * + */ +static char *imap_atom(const char *str, bool escape_only) +{ + struct dynbuf line; + size_t nclean; + size_t len; -/* SASL parameters for the imap protocol */ -static const struct SASLproto saslimap = { - "imap", /* The service name */ - imap_perform_authenticate, /* Send authentication command */ - imap_continue_authenticate, /* Send authentication continuation */ - imap_cancel_authenticate, /* Send authentication cancellation */ - imap_get_message, /* Get SASL response message */ - 0, /* No maximum initial response length */ - '+', /* Code received when continuation is expected */ - IMAP_RESP_OK, /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ -}; + if(!str) + return NULL; -struct ulbits { - int bit; - const char *flag; -}; + len = strlen(str); + nclean = strcspn(str, "() {%*]\\\""); + if(len == nclean) + /* nothing to escape, return a strdup */ + return curlx_strdup(str); + + curlx_dyn_init(&line, 2000); + + if(!escape_only && curlx_dyn_addn(&line, "\"", 1)) + return NULL; + + while(*str) { + if((*str == '\\' || *str == '"') && + curlx_dyn_addn(&line, "\\", 1)) + return NULL; + if(curlx_dyn_addn(&line, str, 1)) + return NULL; + str++; + } + + if(!escape_only && curlx_dyn_addn(&line, "\"", 1)) + return NULL; + + return curlx_dyn_ptr(&line); +} + +/* + * Finds the start of a literal '{size}' in line, skipping over quoted strings. + */ +static const char *imap_find_literal(const char *line, size_t len) +{ + const char *end = line + len; + bool in_quote = FALSE; + + while(line < end) { + if(in_quote) { + if(*line == '\\' && (line + 1) < end) { + line += 2; + continue; + } + if(*line == '"') + in_quote = FALSE; + } + else { + if(*line == '"') + in_quote = TRUE; + else if(*line == '{') + return line; + } + line++; + } + return NULL; +} /*********************************************************************** * @@ -433,13 +437,12 @@ static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out) if(len > 2) { /* Find the start of the message */ len -= 2; - for(message += 2; *message == ' ' || *message == '\t'; message++, len--) + for(message += 2; ISBLANK(*message); message++, len--) ; /* Find the end of the message */ while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') + if(!ISNEWLINE(message[len]) && !ISBLANK(message[len])) break; /* Terminate the message */ @@ -463,7 +466,7 @@ static void imap_state(struct Curl_easy *data, struct imap_conn *imapc, imapstate newstate) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* for debug purposes */ static const char * const names[] = { "STOP", @@ -487,8 +490,9 @@ static void imap_state(struct Curl_easy *data, if(imapc->state != newstate) infof(data, "IMAP %p state change from %s to %s", (void *)imapc, names[imapc->state], names[newstate]); -#endif +#else (void)data; +#endif imapc->state = newstate; } @@ -555,13 +559,13 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, if(result) goto out; /* Change the connection handler */ - conn->handler = &Curl_handler_imaps; + conn->scheme = &Curl_scheme_imaps; } DEBUGASSERT(!imapc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + result, ssldone)); if(!result && ssldone) { imapc->ssldone = ssldone; /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */ @@ -706,7 +710,8 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, } /* Calculate the SASL login details */ - result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress); + result = Curl_sasl_start(&imapc->sasl, data, (bool)imapc->ir_supported, + &progress); if(!result) { if(progress == SASL_INPROGRESS) @@ -859,22 +864,24 @@ static CURLcode imap_perform_append(struct Curl_easy *data, #ifndef CURL_DISABLE_MIME /* Prepare the mime data if some. */ - if(data->set.mimepost.kind != MIMEKIND_NONE) { + if(IS_MIME_POST(data)) { + curl_mimepart *postp = data->set.mimepostp; + /* Use the whole structure as data. */ - data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; + postp->flags &= ~(unsigned int)MIME_BODY_ONLY; /* Add external headers and mime version. */ - curl_mime_headers(&data->set.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, + curl_mime_headers(postp, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, postp, NULL, NULL, MIMESTRATEGY_MAIL); if(!result) if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) - result = Curl_mime_add_header(&data->set.mimepost.curlheaders, + result = Curl_mime_add_header(&postp->curlheaders, "Mime-Version: 1.0"); if(!result) - result = Curl_creader_set_mime(data, &data->set.mimepost); + result = Curl_creader_set_mime(data, postp); if(result) return result; data->state.infilesize = Curl_creader_client_length(data); @@ -1029,20 +1036,15 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, /* Loop through the data line */ for(;;) { size_t wordlen; - while(*line && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - + while(*line && (ISBLANK(*line) || ISNEWLINE(*line))) line++; - } if(!*line) break; /* Extract the word */ - for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) + for(wordlen = 0; line[wordlen] && !ISBLANK(line[wordlen]) && + !ISNEWLINE(line[wordlen]);) wordlen++; /* Does the server support the STARTTLS capability? */ @@ -1139,7 +1141,7 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data, imap_state(data, imapc, IMAP_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ - if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) + if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) /* Perform clear text authentication */ result = imap_perform_login(data, imapc, data->conn); else { @@ -1174,6 +1176,43 @@ static CURLcode imap_state_login_resp(struct Curl_easy *data, return result; } +/* Detect IMAP listings vs. downloading a single email */ +static bool is_custom_fetch_listing_match(const char *params) +{ + /* match " 1:* (FLAGS ..." or " 1,2,3 (FLAGS ..." */ + if(*params++ != ' ') + return FALSE; + + while(ISDIGIT(*params)) { + params++; + if(*params == 0) + return FALSE; + } + if(*params == ':') + return true; + if(*params == ',') + return true; + return FALSE; +} + +static bool is_custom_fetch_listing(struct IMAP *imap) +{ + /* filter out "UID FETCH 1:* (FLAGS ..." queries to list emails */ + if(!imap->custom) + return FALSE; + else if(curl_strequal(imap->custom, "FETCH") && imap->custom_params) { + const char *p = imap->custom_params; + return is_custom_fetch_listing_match(p); + } + else if(curl_strequal(imap->custom, "UID") && imap->custom_params) { + if(curl_strnequal(imap->custom_params, " FETCH ", 7)) { + const char *p = imap->custom_params + 6; + return is_custom_fetch_listing_match(p); + } + } + return FALSE; +} + /* For LIST and SEARCH responses */ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data, struct imap_conn *imapc, @@ -1181,17 +1220,24 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data, imapstate instate) { CURLcode result = CURLE_OK; - char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); + const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); size_t len = imapc->pp.nfinal; + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + DEBUGASSERT(imap); + if(!imap) + return CURLE_FAILED_INIT; (void)instate; - if(imapcode == '*') { + if(imapcode == '*' && is_custom_fetch_listing(imap)) { + /* custom FETCH or UID FETCH for listing is not handled here */ + } + else if(imapcode == '*') { /* Check if this response contains a literal (e.g. FETCH responses with body data). Literal syntax is {size}\r\n */ const char *cr = memchr(line, '\r', len); size_t line_len = cr ? (size_t)(cr - line) : len; - const char *ptr = memchr(line, '{', line_len); + const char *ptr = imap_find_literal(line, line_len); if(ptr) { curl_off_t size = 0; bool parsed = FALSE; @@ -1276,12 +1322,12 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data, imap_state(data, imapc, IMAP_STOP); } else { - /* Failed to parse literal, just write the line */ + /* Failed to parse literal, write the line */ result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); } } else { - /* No literal, just write the line as-is */ + /* No literal, write the line as-is */ result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); } } @@ -1370,7 +1416,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse the continuation data contained within the curly brackets */ - ptr = memchr(ptr, '{', len); + ptr = imap_find_literal(ptr, len); if(ptr) { ptr++; if(!curlx_str_number(&ptr, &size, CURL_OFF_T_MAX) && @@ -1409,7 +1455,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, infof(data, "Written %zu bytes, %" FMT_OFF_TU " bytes are left for transfer", chunk, size - chunk); - /* Have we used the entire overflow or just part of it?*/ + /* Have we used the entire overflow or part of it?*/ if(pp->overflow > chunk) { /* remember the remaining trailing overflow data */ pp->overflow -= chunk; @@ -1517,7 +1563,6 @@ static CURLcode imap_pp_statemachine(struct Curl_easy *data, struct pingpong *pp; size_t nread = 0; - (void)data; if(!imapc || !imap) return CURLE_FAILED_INIT; pp = &imapc->pp; @@ -1649,25 +1694,278 @@ static CURLcode imap_pollset(struct Curl_easy *data, return imapc ? Curl_pp_pollset(data, &imapc->pp, ps) : CURLE_OK; } +static void imap_easy_reset(struct IMAP *imap) +{ + Curl_safefree(imap->mailbox); + Curl_safefree(imap->uid); + Curl_safefree(imap->mindex); + Curl_safefree(imap->section); + Curl_safefree(imap->partial); + Curl_safefree(imap->query); + Curl_safefree(imap->custom); + Curl_safefree(imap->custom_params); + /* Clear the transfer mode for the next request */ + imap->transfer = PPTRANSFER_BODY; +} + /*********************************************************************** * - * imap_connect() - * - * This function should do everything that is to be considered a part of the - * connection phase. + * imap_is_bchar() * - * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE if not. + * Portable test of whether the specified char is a "bchar" as defined in the + * grammar of RFC-5092. */ -static CURLcode imap_connect(struct Curl_easy *data, bool *done) +static bool imap_is_bchar(char ch) { - struct imap_conn *imapc = - Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN); - CURLcode result = CURLE_OK; - - *done = FALSE; /* default to not done yet */ - if(!imapc) - return CURLE_FAILED_INIT; + /* Performing the alnum check first with macro is faster because of ASCII + arithmetic */ + return ch && (ISALNUM(ch) || strchr(":@/&=-._~!$\'()*+,%", ch)); +} + +/*********************************************************************** + * + * imap_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode imap_parse_url_options(struct connectdata *conn, + struct imap_conn *imapc) +{ + CURLcode result = CURLE_OK; + const char *ptr = conn->options; + bool prefer_login = FALSE; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(curl_strnequal(key, "AUTH=+LOGIN", 11)) { + /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */ + prefer_login = TRUE; + imapc->sasl.prefmech = SASL_AUTH_NONE; + } + else if(curl_strnequal(key, "AUTH=", 5)) { + prefer_login = FALSE; + result = Curl_sasl_parse_url_auth_option(&imapc->sasl, + value, ptr - value); + } + else { + prefer_login = FALSE; + result = CURLE_URL_MALFORMAT; + } + + if(*ptr == ';') + ptr++; + } + + if(prefer_login) + imapc->preftype = IMAP_TYPE_CLEARTEXT; + else { + switch(imapc->sasl.prefmech) { + case SASL_AUTH_NONE: + imapc->preftype = IMAP_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + imapc->preftype = IMAP_TYPE_ANY; + break; + default: + imapc->preftype = IMAP_TYPE_SASL; + break; + } + } + + return result; +} + +/*********************************************************************** + * + * imap_parse_url_path() + * + * Parse the URL path into separate path components. + * + */ +static CURLcode imap_parse_url_path(struct Curl_easy *data, + struct IMAP *imap) +{ + /* The imap struct is already initialised in imap_connect() */ + CURLcode result = CURLE_OK; + const char *begin = &data->state.up.path[1]; /* skip leading slash */ + const char *ptr = begin; + + /* See how much of the URL is a valid path and decode it */ + while(imap_is_bchar(*ptr)) + ptr++; + + if(ptr != begin) { + /* Remove the trailing slash if present */ + const char *end = ptr; + if(end > begin && end[-1] == '/') + end--; + + result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL, + REJECT_CTRL); + if(result) + return result; + } + else + imap->mailbox = NULL; + + /* There can be any number of parameters in the form ";NAME=VALUE" */ + while(*ptr == ';') { + char *name; + char *value; + size_t valuelen; + + /* Find the length of the name parameter */ + begin = ++ptr; + while(*ptr && *ptr != '=') + ptr++; + + if(!*ptr) + return CURLE_URL_MALFORMAT; + + /* Decode the name parameter */ + result = Curl_urldecode(begin, ptr - begin, &name, NULL, + REJECT_CTRL); + if(result) + return result; + + /* Find the length of the value parameter */ + begin = ++ptr; + while(imap_is_bchar(*ptr)) + ptr++; + + /* Decode the value parameter */ + result = Curl_urldecode(begin, ptr - begin, &value, &valuelen, + REJECT_CTRL); + if(result) { + curlx_free(name); + return result; + } + + DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value)); + + /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION + and PARTIAL) stripping of the trailing slash character if it is + present. + + Note: Unknown parameters trigger a URL_MALFORMAT error. */ + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + if(valuelen) { + if(curl_strequal(name, "UIDVALIDITY") && !imap->uidvalidity_set) { + curl_off_t num; + const char *p = (const char *)value; + if(!curlx_str_number(&p, &num, UINT_MAX)) { + imap->uidvalidity = (unsigned int)num; + imap->uidvalidity_set = TRUE; + } + curlx_free(value); + } + else if(curl_strequal(name, "UID") && !imap->uid) { + imap->uid = value; + } + else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) { + imap->mindex = value; + } + else if(curl_strequal(name, "SECTION") && !imap->section) { + imap->section = value; + } + else if(curl_strequal(name, "PARTIAL") && !imap->partial) { + imap->partial = value; + } + else { + curlx_free(name); + curlx_free(value); + return CURLE_URL_MALFORMAT; + } + } + else + /* blank? */ + curlx_free(value); + curlx_free(name); + } + + /* Does the URL contain a query parameter? Only valid when we have a mailbox + and no UID as per RFC-5092 */ + if(imap->mailbox && !imap->uid && !imap->mindex) { + /* Get the query parameter, URL decoded */ + CURLUcode uc = curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query, + CURLU_URLDECODE); + if(uc == CURLUE_OUT_OF_MEMORY) + return CURLE_OUT_OF_MEMORY; + } + + /* Any extra stuff at the end of the URL is an error */ + if(*ptr) + return CURLE_URL_MALFORMAT; + + return CURLE_OK; +} + +/*********************************************************************** + * + * imap_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode imap_parse_custom_request(struct Curl_easy *data, + struct IMAP *imap) +{ + CURLcode result = CURLE_OK; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + if(custom) { + /* URL decode the custom request */ + result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL); + + /* Extract the parameters if specified */ + if(!result) { + const char *params = imap->custom; + + while(*params && *params != ' ') + params++; + + if(*params) { + imap->custom_params = curlx_strdup(params); + imap->custom[params - imap->custom] = '\0'; + + if(!imap->custom_params) + result = CURLE_OUT_OF_MEMORY; + } + } + } + + return result; +} + +/*********************************************************************** + * + * imap_connect() + * + * This function should do everything that is to be considered a part of the + * connection phase. + * + * The variable 'done' points to will be TRUE if the protocol-layer connect + * phase is done when this function returns, or FALSE if not. + */ +static CURLcode imap_connect(struct Curl_easy *data, bool *done) +{ + struct imap_conn *imapc = + Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN); + CURLcode result = CURLE_OK; + + *done = FALSE; /* default to not done yet */ + if(!imapc) + return CURLE_FAILED_INIT; /* Parse the URL options */ result = imap_parse_url_options(data->conn, imapc); @@ -1678,7 +1976,7 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done) imap_state(data, imapc, IMAP_SERVERGREET); /* Start off with an response id of '*' */ - curlx_strcopy(imapc->resptag, sizeof(imapc->resptag), "*", 1); + curlx_strcopy(imapc->resptag, sizeof(imapc->resptag), STRCONST("*")); result = imap_multi_statemach(data, done); @@ -1810,17 +2108,63 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected, return result; } -/*********************************************************************** - * - * imap_do() - * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (imap_perform). - * - * The input argument is already checked for validity. - */ -static CURLcode imap_do(struct Curl_easy *data, bool *done) -{ +/* Call this when the DO phase has completed */ +static CURLcode imap_dophase_done(struct Curl_easy *data, + struct IMAP *imap, + bool connected) +{ + (void)connected; + + if(imap->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + return CURLE_OK; +} + +/*********************************************************************** + * + * imap_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + */ +static CURLcode imap_regular_transfer(struct Curl_easy *data, + struct IMAP *imap, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + + /* Make sure size is unknown at this point */ + data->req.size = -1; + + /* Set the progress data */ + Curl_pgrsReset(data); + + /* Carry out the perform */ + result = imap_perform(data, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = imap_dophase_done(data, imap, connected); + + return result; +} + +/*********************************************************************** + * + * imap_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (imap_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode imap_do(struct Curl_easy *data, bool *done) +{ struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); CURLcode result = CURLE_OK; *done = FALSE; /* default to false */ @@ -1854,7 +2198,6 @@ static CURLcode imap_disconnect(struct Curl_easy *data, { struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); - (void)data; if(imapc) { /* We cannot send quit unconditionally. If this connection is stale or bad in any way (pingpong has pending data to send), @@ -1869,20 +2212,6 @@ static CURLcode imap_disconnect(struct Curl_easy *data, return CURLE_OK; } -/* Call this when the DO phase has completed */ -static CURLcode imap_dophase_done(struct Curl_easy *data, - struct IMAP *imap, - bool connected) -{ - (void)connected; - - if(imap->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_xfer_setup_nop(data); - - return CURLE_OK; -} - /* Called from multi.c while DOing */ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done) { @@ -1904,52 +2233,6 @@ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done) return result; } -/*********************************************************************** - * - * imap_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - */ -static CURLcode imap_regular_transfer(struct Curl_easy *data, - struct IMAP *imap, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - - /* Make sure size is unknown at this point */ - data->req.size = -1; - - /* Set the progress data */ - Curl_pgrsReset(data); - - /* Carry out the perform */ - result = imap_perform(data, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = imap_dophase_done(data, imap, connected); - - return result; -} - -static void imap_easy_reset(struct IMAP *imap) -{ - Curl_safefree(imap->mailbox); - Curl_safefree(imap->uid); - Curl_safefree(imap->mindex); - Curl_safefree(imap->section); - Curl_safefree(imap->partial); - Curl_safefree(imap->query); - Curl_safefree(imap->custom); - Curl_safefree(imap->custom_params); - /* Clear the transfer mode for the next request */ - imap->transfer = PPTRANSFER_BODY; -} - static void imap_easy_dtor(void *key, size_t klen, void *entry) { struct IMAP *imap = entry; @@ -1970,6 +2253,20 @@ static void imap_conn_dtor(void *key, size_t klen, void *entry) curlx_free(imapc); } +/* SASL parameters for the imap protocol */ +static const struct SASLproto saslimap = { + "imap", /* The service name */ + imap_perform_authenticate, /* Send authentication command */ + imap_continue_authenticate, /* Send authentication continuation */ + imap_cancel_authenticate, /* Send authentication cancellation */ + imap_get_message, /* Get SASL response message */ + 0, /* No maximum initial response length */ + '+', /* Code received when continuation is expected */ + IMAP_RESP_OK, /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + static CURLcode imap_setup_connection(struct Curl_easy *data, struct connectdata *conn) { @@ -2002,361 +2299,62 @@ static CURLcode imap_setup_connection(struct Curl_easy *data, return CURLE_OK; } -/*********************************************************************** - * - * imap_sendf() - * - * Sends the formatted string as an IMAP command to the server. - * - * Designed to never block. - */ -static CURLcode imap_sendf(struct Curl_easy *data, - struct imap_conn *imapc, - const char *fmt, ...) -{ - CURLcode result = CURLE_OK; - - DEBUGASSERT(fmt); - - /* Calculate the tag based on the connection ID and command ID */ - curl_msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", - 'A' + curlx_sltosi((long)(data->conn->connection_id % 26)), - ++imapc->cmdid); - - /* start with a blank buffer */ - curlx_dyn_reset(&imapc->dyn); - - /* append tag + space + fmt */ - result = curlx_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt); - if(!result) { - va_list ap; - va_start(ap, fmt); -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#endif - result = Curl_pp_vsendf(data, &imapc->pp, curlx_dyn_ptr(&imapc->dyn), ap); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - va_end(ap); - } - return result; -} - -/*********************************************************************** - * - * imap_atom() - * - * Checks the input string for characters that need escaping and returns an - * atom ready for sending to the server. - * - * The returned string needs to be freed. - * - */ -static char *imap_atom(const char *str, bool escape_only) -{ - struct dynbuf line; - size_t nclean; - size_t len; - - if(!str) - return NULL; - - len = strlen(str); - nclean = strcspn(str, "() {%*]\\\""); - if(len == nclean) - /* nothing to escape, return a strdup */ - return curlx_strdup(str); - - curlx_dyn_init(&line, 2000); - - if(!escape_only && curlx_dyn_addn(&line, "\"", 1)) - return NULL; - - while(*str) { - if((*str == '\\' || *str == '"') && - curlx_dyn_addn(&line, "\\", 1)) - return NULL; - if(curlx_dyn_addn(&line, str, 1)) - return NULL; - str++; - } - - if(!escape_only && curlx_dyn_addn(&line, "\"", 1)) - return NULL; - - return curlx_dyn_ptr(&line); -} - -/*********************************************************************** - * - * imap_is_bchar() - * - * Portable test of whether the specified char is a "bchar" as defined in the - * grammar of RFC-5092. - */ -static bool imap_is_bchar(char ch) -{ - /* Performing the alnum check with this macro is faster because of ASCII - arithmetic */ - if(ISALNUM(ch)) - return TRUE; - - switch(ch) { - /* bchar */ - case ':': - case '@': - case '/': - /* bchar -> achar */ - case '&': - case '=': - /* bchar -> achar -> uchar -> unreserved (without alphanumeric) */ - case '-': - case '.': - case '_': - case '~': - /* bchar -> achar -> uchar -> sub-delims-sh */ - case '!': - case '$': - case '\'': - case '(': - case ')': - case '*': - case '+': - case ',': - /* bchar -> achar -> uchar -> pct-encoded */ - case '%': /* HEXDIG chars are already included above */ - return TRUE; - - default: - return FALSE; - } -} - -/*********************************************************************** - * - * imap_parse_url_options() - * - * Parse the URL login options. +/* + * IMAP protocol. */ -static CURLcode imap_parse_url_options(struct connectdata *conn, - struct imap_conn *imapc) -{ - CURLcode result = CURLE_OK; - const char *ptr = conn->options; - bool prefer_login = FALSE; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(curl_strnequal(key, "AUTH=+LOGIN", 11)) { - /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */ - prefer_login = TRUE; - imapc->sasl.prefmech = SASL_AUTH_NONE; - } - else if(curl_strnequal(key, "AUTH=", 5)) { - prefer_login = FALSE; - result = Curl_sasl_parse_url_auth_option(&imapc->sasl, - value, ptr - value); - } - else { - prefer_login = FALSE; - result = CURLE_URL_MALFORMAT; - } - - if(*ptr == ';') - ptr++; - } - - if(prefer_login) - imapc->preftype = IMAP_TYPE_CLEARTEXT; - else { - switch(imapc->sasl.prefmech) { - case SASL_AUTH_NONE: - imapc->preftype = IMAP_TYPE_NONE; - break; - case SASL_AUTH_DEFAULT: - imapc->preftype = IMAP_TYPE_ANY; - break; - default: - imapc->preftype = IMAP_TYPE_SASL; - break; - } - } +static const struct Curl_protocol Curl_protocol_imap = { + imap_setup_connection, /* setup_connection */ + imap_do, /* do_it */ + imap_done, /* done */ + ZERO_NULL, /* do_more */ + imap_connect, /* connect_it */ + imap_multi_statemach, /* connecting */ + imap_doing, /* doing */ + imap_pollset, /* proto_pollset */ + imap_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + imap_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; - return result; -} +#endif /* CURL_DISABLE_IMAP */ -/*********************************************************************** - * - * imap_parse_url_path() - * - * Parse the URL path into separate path components. - * +/* + * IMAP protocol handler. */ -static CURLcode imap_parse_url_path(struct Curl_easy *data, - struct IMAP *imap) -{ - /* The imap struct is already initialised in imap_connect() */ - CURLcode result = CURLE_OK; - const char *begin = &data->state.up.path[1]; /* skip leading slash */ - const char *ptr = begin; - - /* See how much of the URL is a valid path and decode it */ - while(imap_is_bchar(*ptr)) - ptr++; - - if(ptr != begin) { - /* Remove the trailing slash if present */ - const char *end = ptr; - if(end > begin && end[-1] == '/') - end--; - - result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL, - REJECT_CTRL); - if(result) - return result; - } - else - imap->mailbox = NULL; - - /* There can be any number of parameters in the form ";NAME=VALUE" */ - while(*ptr == ';') { - char *name; - char *value; - size_t valuelen; - - /* Find the length of the name parameter */ - begin = ++ptr; - while(*ptr && *ptr != '=') - ptr++; - - if(!*ptr) - return CURLE_URL_MALFORMAT; - - /* Decode the name parameter */ - result = Curl_urldecode(begin, ptr - begin, &name, NULL, - REJECT_CTRL); - if(result) - return result; - - /* Find the length of the value parameter */ - begin = ++ptr; - while(imap_is_bchar(*ptr)) - ptr++; - - /* Decode the value parameter */ - result = Curl_urldecode(begin, ptr - begin, &value, &valuelen, - REJECT_CTRL); - if(result) { - curlx_free(name); - return result; - } - - DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value)); - - /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION - and PARTIAL) stripping of the trailing slash character if it is - present. - - Note: Unknown parameters trigger a URL_MALFORMAT error. */ - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - if(valuelen) { - if(curl_strequal(name, "UIDVALIDITY") && !imap->uidvalidity_set) { - curl_off_t num; - const char *p = (const char *)value; - if(!curlx_str_number(&p, &num, UINT_MAX)) { - imap->uidvalidity = (unsigned int)num; - imap->uidvalidity_set = TRUE; - } - curlx_free(value); - } - else if(curl_strequal(name, "UID") && !imap->uid) { - imap->uid = value; - } - else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) { - imap->mindex = value; - } - else if(curl_strequal(name, "SECTION") && !imap->section) { - imap->section = value; - } - else if(curl_strequal(name, "PARTIAL") && !imap->partial) { - imap->partial = value; - } - else { - curlx_free(name); - curlx_free(value); - return CURLE_URL_MALFORMAT; - } - } - else - /* blank? */ - curlx_free(value); - curlx_free(name); - } - - /* Does the URL contain a query parameter? Only valid when we have a mailbox - and no UID as per RFC-5092 */ - if(imap->mailbox && !imap->uid && !imap->mindex) { - /* Get the query parameter, URL decoded */ - CURLUcode uc = curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query, - CURLU_URLDECODE); - if(uc == CURLUE_OUT_OF_MEMORY) - return CURLE_OUT_OF_MEMORY; - } - - /* Any extra stuff at the end of the URL is an error */ - if(*ptr) - return CURLE_URL_MALFORMAT; - - return CURLE_OK; -} +const struct Curl_scheme Curl_scheme_imap = { + "imap", /* scheme */ +#ifdef CURL_DISABLE_IMAP + ZERO_NULL, +#else + &Curl_protocol_imap, +#endif + CURLPROTO_IMAP, /* protocol */ + CURLPROTO_IMAP, /* family */ + PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | + PROTOPT_CONN_REUSE, + PORT_IMAP, /* defport */ +}; -/*********************************************************************** - * - * imap_parse_custom_request() - * - * Parse the custom request. +/* + * IMAPS protocol handler. */ -static CURLcode imap_parse_custom_request(struct Curl_easy *data, - struct IMAP *imap) -{ - CURLcode result = CURLE_OK; - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - - if(custom) { - /* URL decode the custom request */ - result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL); - - /* Extract the parameters if specified */ - if(!result) { - const char *params = imap->custom; - - while(*params && *params != ' ') - params++; - - if(*params) { - imap->custom_params = curlx_strdup(params); - imap->custom[params - imap->custom] = '\0'; - - if(!imap->custom_params) - result = CURLE_OUT_OF_MEMORY; - } - } - } - - return result; -} - -#endif /* CURL_DISABLE_IMAP */ +const struct Curl_scheme Curl_scheme_imaps = { + "imaps", /* scheme */ +#if defined(CURL_DISABLE_IMAP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_imap, +#endif + CURLPROTO_IMAPS, /* protocol */ + CURLPROTO_IMAP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_IMAPS, /* defport */ +}; diff --git a/vendor/curl/lib/imap.h b/vendor/curl/lib/imap.h index 26306a6..f179991 100644 --- a/vendor/curl/lib/imap.h +++ b/vendor/curl/lib/imap.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_handler Curl_handler_imap; -extern const struct Curl_handler Curl_handler_imaps; +extern const struct Curl_scheme Curl_scheme_imap; +extern const struct Curl_scheme Curl_scheme_imaps; /* Authentication type flags */ #define IMAP_TYPE_CLEARTEXT (1 << 0) diff --git a/vendor/curl/lib/ldap.c b/vendor/curl/lib/ldap.c index 95a982f..e223078 100644 --- a/vendor/curl/lib/ldap.c +++ b/vendor/curl/lib/ldap.c @@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "curl_ldap.h" #if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP) @@ -41,41 +43,27 @@ * OpenLDAP library versions, USE_OPENLDAP shall not be defined. */ -/* Wincrypt must be included before anything that could include OpenSSL. */ -#ifdef USE_WIN32_CRYPTO -#include -/* Undefine wincrypt conflicting symbols for BoringSSL. */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#endif - #ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ -# ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable:4201) -# endif -# include /* for [P]UNICODE_STRING */ -# ifdef _MSC_VER -# pragma warning(pop) -# endif -# include -# include +# include +/* Undefine indirect symbols conflicting with BoringSSL/AWS-LC. */ +# undef X509_NAME +# undef X509_EXTENSIONS +# undef PKCS7_ISSUER_AND_SERIAL +# undef PKCS7_SIGNER_INFO +# undef OCSP_REQUEST +# undef OCSP_RESPONSE +# include #else -# define LDAP_DEPRECATED 1 /* Be sure ldap_init() is defined. */ -# ifdef HAVE_LBER_H -# include -# endif -# include -# if defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H) -# include -# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */ +# define LDAP_DEPRECATED 1 /* Be sure ldap_init() is defined. */ +# ifdef HAVE_LBER_H +# include +# endif +# include +# if defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H) +# include +# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */ #endif -#include "urldata.h" #include "cfilters.h" #include "sendf.h" #include "curl_trc.h" @@ -84,7 +72,6 @@ #include "transfer.h" #include "curlx/strparse.h" #include "bufref.h" -#include "curl_ldap.h" #include "curlx/multibyte.h" #include "curlx/base64.h" #include "connect.h" @@ -93,7 +80,7 @@ #define FREE_ON_WINLDAP(x) curlx_free(x) #define curl_ldap_num_t ULONG #else -#define FREE_ON_WINLDAP(x) +#define FREE_ON_WINLDAP(x) do {} while(0) #define curl_ldap_num_t int #endif @@ -163,66 +150,6 @@ static void ldap_trace_low(const char *fmt, ...) CURL_PRINTF(1, 2); #define LDAP_OPT_OFF ((void *)(size_t)0) #endif -static CURLcode ldap_do(struct Curl_easy *data, bool *done); - -/* - * LDAP protocol handler. - */ -const struct Curl_handler Curl_handler_ldap = { - "ldap", /* scheme */ - ZERO_NULL, /* setup_connection */ - ldap_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_LDAP, /* defport */ - CURLPROTO_LDAP, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL_REUSE /* flags */ -}; - -#ifdef HAVE_LDAP_SSL -/* - * LDAPS protocol handler. - */ -const struct Curl_handler Curl_handler_ldaps = { - "ldaps", /* scheme */ - ZERO_NULL, /* setup_connection */ - ldap_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_LDAPS, /* defport */ - CURLPROTO_LDAPS, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL /* flags */ -}; -#endif - #ifdef USE_WIN32_LDAP #ifdef USE_WINDOWS_SSPI @@ -398,7 +325,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); #else /* !USE_WIN32_LDAP */ int ldap_option; - char *ldap_ca = conn->ssl_config.CAfile; + const char *ldap_ca = conn->ssl_config.CAfile; #ifdef LDAP_OPT_X_TLS if(conn->ssl_config.verifypeer) { /* OpenLDAP SDK supports BASE64 files. */ @@ -489,7 +416,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) Curl_pgrsReset(data); rc = ldap_search_s(server, ludp->lud_dn, - (curl_ldap_num_t)ludp->lud_scope, + ludp->lud_scope, ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); if(rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED) { @@ -1028,29 +955,81 @@ void Curl_ldap_version(char *buf, size_t bufsz) unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); unsigned int minor = - (((unsigned int)api.ldapai_vendor_version - major * 10000) + (((unsigned int)api.ldapai_vendor_version - (major * 10000)) - patch) / 100; - #ifdef __OS400__ - curl_msnprintf(buf, bufsz, "IBMLDAP/%u.%u.%u", - major, minor, patch); - + curl_msnprintf(buf, bufsz, "IBMLDAP/%u.%u.%u", major, minor, patch); ldap_value_free(api.ldapai_extensions); + (void)flavor; #else curl_msnprintf(buf, bufsz, "%s/%u.%u.%u%s", api.ldapai_vendor_name, major, minor, patch, flavor); - ldap_memfree(api.ldapai_vendor_name); ber_memvfree((void **)api.ldapai_extensions); #endif } else curl_msnprintf(buf, bufsz, "LDAP/1"); -#endif +#endif /* USE_WIN32_LDAP */ } +/* + * LDAP protocol handler. + */ +const struct Curl_protocol Curl_protocol_ldap = { + ZERO_NULL, /* setup_connection */ + ldap_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #if defined(__GNUC__) && defined(__APPLE__) #pragma GCC diagnostic pop #endif #endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */ + +/* + * LDAP + */ +const struct Curl_scheme Curl_scheme_ldap = { + "ldap", /* scheme */ +#ifdef CURL_DISABLE_LDAP + ZERO_NULL, +#else + &Curl_protocol_ldap, +#endif + CURLPROTO_LDAP, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_SSL_REUSE, /* flags */ + PORT_LDAP, /* defport */ +}; + +/* + * LDAPS + */ +const struct Curl_scheme Curl_scheme_ldaps = { + "ldaps", /* scheme */ +#if defined(CURL_DISABLE_LDAP) || !defined(HAVE_LDAP_SSL) + ZERO_NULL, +#else + &Curl_protocol_ldap, +#endif + CURLPROTO_LDAPS, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_SSL, /* flags */ + PORT_LDAPS, /* defport */ +}; diff --git a/vendor/curl/lib/llist.h b/vendor/curl/lib/llist.h index 1d73db1..09c695d 100644 --- a/vendor/curl/lib/llist.h +++ b/vendor/curl/lib/llist.h @@ -50,13 +50,13 @@ struct Curl_llist_node { #endif }; -void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor); -void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_node *, - const void *, struct Curl_llist_node *node); -void Curl_llist_append(struct Curl_llist *, const void *, - struct Curl_llist_node *node); -void Curl_node_remove(struct Curl_llist_node *); -void Curl_llist_destroy(struct Curl_llist *, void *); +void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor); +void Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_node *e, + const void *p, struct Curl_llist_node *ne); +void Curl_llist_append(struct Curl_llist *list, const void *p, + struct Curl_llist_node *ne); +void Curl_node_remove(struct Curl_llist_node *e); +void Curl_llist_destroy(struct Curl_llist *list, void *user); /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which might be NULL */ @@ -74,7 +74,7 @@ void *Curl_node_elem(struct Curl_llist_node *n); /* Remove the node from the list and return the custom data * from a Curl_llist_node. Will NOT invoke a registered `dtor`. */ -void *Curl_node_take_elem(struct Curl_llist_node *); +void *Curl_node_take_elem(struct Curl_llist_node *e); /* Curl_node_next() returns the next element in a list from a given Curl_llist_node */ diff --git a/vendor/curl/lib/md4.c b/vendor/curl/lib/md4.c index e7ecf58..1ac6ef1 100644 --- a/vendor/curl/lib/md4.c +++ b/vendor/curl/lib/md4.c @@ -40,17 +40,24 @@ #ifdef USE_WOLFSSL #include -#define VOID_MD4_INIT -#ifdef NO_MD4 -#define WOLFSSL_NO_MD4 -#endif #endif /* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ -#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) + +#if defined(USE_WOLFSSL) && !defined(NO_MD4) #include +#define VOID_MD4_INIT + +#ifdef OPENSSL_COEXIST +# define MD4_CTX WOLFSSL_MD4_CTX +# define MD4_Init wolfSSL_MD4_Init +# define MD4_Update wolfSSL_MD4_Update +# define MD4_Final wolfSSL_MD4_Final +#endif + #elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) #include + #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ @@ -59,26 +66,8 @@ (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) -#define AN_APPLE_OS #include -#elif defined(USE_WIN32_CRYPTO) -#include -#elif defined(USE_GNUTLS) -#include -#endif - -#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) - -#ifdef OPENSSL_COEXIST - #define MD4_CTX WOLFSSL_MD4_CTX - #define MD4_Init wolfSSL_MD4_Init - #define MD4_Update wolfSSL_MD4_Update - #define MD4_Final wolfSSL_MD4_Final -#endif - -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) -#elif defined(AN_APPLE_OS) typedef CC_MD4_CTX MD4_CTX; static int MD4_Init(MD4_CTX *ctx) @@ -86,17 +75,18 @@ static int MD4_Init(MD4_CTX *ctx) return CC_MD4_Init(ctx); } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len) { - (void)CC_MD4_Update(ctx, data, (CC_LONG)size); + (void)CC_MD4_Update(ctx, input, (CC_LONG)len); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void MD4_Final(unsigned char *digest, MD4_CTX *ctx) { - (void)CC_MD4_Final(result, ctx); + (void)CC_MD4_Final(digest, ctx); } #elif defined(USE_WIN32_CRYPTO) +#include struct md4_ctx { HCRYPTPROV hCryptProv; @@ -122,18 +112,18 @@ static int MD4_Init(MD4_CTX *ctx) return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len) { - CryptHashData(ctx->hHash, (const BYTE *)data, (unsigned int)size, 0); + CryptHashData(ctx->hHash, (const BYTE *)input, (unsigned int)len, 0); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void MD4_Final(unsigned char *digest, MD4_CTX *ctx) { unsigned long length = 0; CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); if(length == MD4_DIGEST_LENGTH) - CryptGetHashParam(ctx->hHash, HP_HASHVAL, result, &length, 0); + CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); if(ctx->hHash) CryptDestroyHash(ctx->hHash); @@ -143,6 +133,7 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) } #elif defined(USE_GNUTLS) +#include typedef struct md4_ctx MD4_CTX; @@ -152,14 +143,14 @@ static int MD4_Init(MD4_CTX *ctx) return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len) { - md4_update(ctx, size, data); + md4_update(ctx, len, input); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void MD4_Final(unsigned char *digest, MD4_CTX *ctx) { - md4_digest(ctx, MD4_DIGEST_SIZE, result); + md4_digest(ctx, MD4_DIGEST_SIZE, digest); } #else @@ -170,7 +161,7 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) * MD4 Message-Digest Algorithm (RFC 1320). * * Homepage: - https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + * https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 * * Author: * Alexander Peslyak, better known as Solar Designer @@ -179,8 +170,8 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) * claimed, and the software is hereby placed in the public domain. In case * this attempt to disclaim copyright and place the software in the public * domain is deemed null and void, then the software is Copyright (c) 2001 - * Alexander Peslyak and it is hereby released to the general public under the - * following terms: + * Alexander Peslyak and it is hereby released to the general public under + * the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. @@ -190,21 +181,14 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) * (This is a heavily cut-down "BSD license".) */ -/* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int MD4_u32plus; - struct md4_ctx { - MD4_u32plus lo, hi; - MD4_u32plus a, b, c, d; + uint32_t lo, hi; + uint32_t a, b, c, d; unsigned char buffer[64]; - MD4_u32plus block[16]; + uint32_t block[16]; }; typedef struct md4_ctx MD4_CTX; -static int MD4_Init(MD4_CTX *ctx); -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size); -static void MD4_Final(unsigned char *result, MD4_CTX *ctx); - /* * The basic MD4 functions. * @@ -219,27 +203,26 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx); * The MD4 transformation for all three rounds. */ #define MD4_STEP(f, a, b, c, d, x, s) \ - (a) += f((b), (c), (d)) + (x); \ + (a) += f(b, c, d) + (x); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); /* * SET reads 4 input bytes in little-endian byte order and stores them * in a properly aligned word in host byte order. * - * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * does not work. + * The check for little-endian architectures that tolerate unaligned memory + * accesses is an optimization. Nothing will break if it does not work. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define MD4_SET(n) (*(const MD4_u32plus *)(const void *)&ptr[(n) * 4]) +#define MD4_SET(n) (*(const uint32_t *)(const void *)&ptr[(n) * 4]) #define MD4_GET(n) MD4_SET(n) #else -#define MD4_SET(n) (ctx->block[(n)] = \ - (MD4_u32plus)ptr[(n) * 4] | \ - ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \ - ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \ - ((MD4_u32plus)ptr[(n) * 4 + 3] << 24)) -#define MD4_GET(n) (ctx->block[(n)]) +#define MD4_SET(n) (ctx->block[n] = \ + (uint32_t)ptr[(n) * 4] | \ + ((uint32_t)ptr[((n) * 4) + 1] << 8) | \ + ((uint32_t)ptr[((n) * 4) + 2] << 16) | \ + ((uint32_t)ptr[((n) * 4) + 3] << 24)) +#define MD4_GET(n) ctx->block[n] #endif /* @@ -247,12 +230,12 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx); * the bit counters. There are no alignment requirements. */ static const void *my_md4_body(MD4_CTX *ctx, - const void *data, unsigned long size) + const void *input, unsigned long size) { const unsigned char *ptr; - MD4_u32plus a, b, c, d; + uint32_t a, b, c, d; - ptr = (const unsigned char *)data; + ptr = (const unsigned char *)input; a = ctx->a; b = ctx->b; @@ -260,7 +243,7 @@ static const void *my_md4_body(MD4_CTX *ctx, d = ctx->d; do { - MD4_u32plus saved_a, saved_b, saved_c, saved_d; + uint32_t saved_a, saved_b, saved_c, saved_d; saved_a = a; saved_b = b; @@ -346,45 +329,46 @@ static int MD4_Init(MD4_CTX *ctx) ctx->lo = 0; ctx->hi = 0; + return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len) { - MD4_u32plus saved_lo; + uint32_t saved_lo; unsigned long used; saved_lo = ctx->lo; - ctx->lo = (saved_lo + size) & 0x1fffffff; + ctx->lo = (saved_lo + len) & 0x1fffffff; if(ctx->lo < saved_lo) ctx->hi++; - ctx->hi += (MD4_u32plus)size >> 29; + ctx->hi += (uint32_t)len >> 29; used = saved_lo & 0x3f; if(used) { unsigned long available = 64 - used; - if(size < available) { - memcpy(&ctx->buffer[used], data, size); + if(len < available) { + memcpy(&ctx->buffer[used], input, len); return; } - memcpy(&ctx->buffer[used], data, available); - data = (const unsigned char *)data + available; - size -= available; + memcpy(&ctx->buffer[used], input, available); + input = (const unsigned char *)input + available; + len -= available; my_md4_body(ctx, ctx->buffer, 64); } - if(size >= 64) { - data = my_md4_body(ctx, data, size & ~(unsigned long)0x3f); - size &= 0x3f; + if(len >= 64) { + input = my_md4_body(ctx, input, len & ~(unsigned long)0x3f); + len &= 0x3f; } - memcpy(ctx->buffer, data, size); + memcpy(ctx->buffer, input, len); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void MD4_Final(unsigned char *digest, MD4_CTX *ctx) { unsigned long used, available; @@ -415,30 +399,30 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) my_md4_body(ctx, ctx->buffer, 64); - result[0] = curlx_ultouc((ctx->a) & 0xff); - result[1] = curlx_ultouc((ctx->a >> 8) & 0xff); - result[2] = curlx_ultouc((ctx->a >> 16) & 0xff); - result[3] = curlx_ultouc(ctx->a >> 24); - result[4] = curlx_ultouc((ctx->b) & 0xff); - result[5] = curlx_ultouc((ctx->b >> 8) & 0xff); - result[6] = curlx_ultouc((ctx->b >> 16) & 0xff); - result[7] = curlx_ultouc(ctx->b >> 24); - result[8] = curlx_ultouc((ctx->c) & 0xff); - result[9] = curlx_ultouc((ctx->c >> 8) & 0xff); - result[10] = curlx_ultouc((ctx->c >> 16) & 0xff); - result[11] = curlx_ultouc(ctx->c >> 24); - result[12] = curlx_ultouc((ctx->d) & 0xff); - result[13] = curlx_ultouc((ctx->d >> 8) & 0xff); - result[14] = curlx_ultouc((ctx->d >> 16) & 0xff); - result[15] = curlx_ultouc(ctx->d >> 24); + digest[0] = curlx_ultouc((ctx->a) & 0xff); + digest[1] = curlx_ultouc((ctx->a >> 8) & 0xff); + digest[2] = curlx_ultouc((ctx->a >> 16) & 0xff); + digest[3] = curlx_ultouc(ctx->a >> 24); + digest[4] = curlx_ultouc((ctx->b) & 0xff); + digest[5] = curlx_ultouc((ctx->b >> 8) & 0xff); + digest[6] = curlx_ultouc((ctx->b >> 16) & 0xff); + digest[7] = curlx_ultouc(ctx->b >> 24); + digest[8] = curlx_ultouc((ctx->c) & 0xff); + digest[9] = curlx_ultouc((ctx->c >> 8) & 0xff); + digest[10] = curlx_ultouc((ctx->c >> 16) & 0xff); + digest[11] = curlx_ultouc(ctx->c >> 24); + digest[12] = curlx_ultouc((ctx->d) & 0xff); + digest[13] = curlx_ultouc((ctx->d >> 8) & 0xff); + digest[14] = curlx_ultouc((ctx->d >> 16) & 0xff); + digest[15] = curlx_ultouc(ctx->d >> 24); memset(ctx, 0, sizeof(*ctx)); } #endif /* CRYPTO LIBS */ -CURLcode Curl_md4it(unsigned char *output, const unsigned char *input, - const size_t len) +CURLcode Curl_md4it(unsigned char *output, + const unsigned char *input, const size_t len) { MD4_CTX ctx; diff --git a/vendor/curl/lib/md5.c b/vendor/curl/lib/md5.c index 3e068cc..c2bd176 100644 --- a/vendor/curl/lib/md5.c +++ b/vendor/curl/lib/md5.c @@ -30,53 +30,23 @@ #include "curl_hmac.h" #ifdef USE_OPENSSL - #include - #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0) - #define USE_OPENSSL_MD5 - #endif +#include #endif #ifdef USE_WOLFSSL - #include - #ifndef NO_MD5 - #define USE_WOLFSSL_MD5 - #endif +#include #endif #ifdef USE_MBEDTLS - #include - #if MBEDTLS_VERSION_NUMBER < 0x03020000 - #error "mbedTLS 3.2.0 or later required" - #endif - #include - #if defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 /* mbedTLS 4+ */ - #define USE_MBEDTLS_MD5 - #endif +#include +#if MBEDTLS_VERSION_NUMBER < 0x03020000 +#error "mbedTLS 3.2.0 or later required" #endif - -#ifdef USE_GNUTLS -#include -#elif defined(USE_OPENSSL_MD5) -#include -#elif defined(USE_WOLFSSL_MD5) -#include -#elif defined(USE_MBEDTLS_MD5) -#include -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ - defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ - (__MAC_OS_X_VERSION_MIN_REQUIRED < 101500)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ - defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ - (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) -#define AN_APPLE_OS -#include -#elif defined(USE_WIN32_CRYPTO) -#include +#include #endif #ifdef USE_GNUTLS +#include typedef struct md5_ctx my_md5_ctx; @@ -87,10 +57,9 @@ static CURLcode my_md5_init(void *ctx) } static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int inputLen) + const unsigned char *input, unsigned int len) { - md5_update(ctx, inputLen, input); + md5_update(ctx, len, input); } static void my_md5_final(unsigned char *digest, void *ctx) @@ -98,8 +67,14 @@ static void my_md5_final(unsigned char *digest, void *ctx) md5_digest(ctx, 16, digest); } -#elif defined(USE_OPENSSL_MD5) || \ - (defined(USE_WOLFSSL_MD5) && !defined(OPENSSL_COEXIST)) +#elif (defined(USE_OPENSSL) && \ + !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0)) || \ + (defined(USE_WOLFSSL) && !defined(NO_MD5) && !defined(OPENSSL_COEXIST)) +#ifdef USE_OPENSSL +#include +#else +#include +#endif typedef MD5_CTX my_md5_ctx; @@ -112,8 +87,7 @@ static CURLcode my_md5_init(void *ctx) } static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int len) + const unsigned char *input, unsigned int len) { (void)MD5_Update(ctx, input, len); } @@ -123,7 +97,8 @@ static void my_md5_final(unsigned char *digest, void *ctx) (void)MD5_Final(digest, ctx); } -#elif defined(USE_WOLFSSL_MD5) +#elif defined(USE_WOLFSSL) && !defined(NO_MD5) +#include typedef WOLFSSL_MD5_CTX my_md5_ctx; @@ -136,8 +111,7 @@ static CURLcode my_md5_init(void *ctx) } static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int len) + const unsigned char *input, unsigned int len) { (void)wolfSSL_MD5_Update(ctx, input, len); } @@ -147,7 +121,9 @@ static void my_md5_final(unsigned char *digest, void *ctx) (void)wolfSSL_MD5_Final(digest, ctx); } -#elif defined(USE_MBEDTLS_MD5) +#elif defined(USE_MBEDTLS) && \ + defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 /* mbedTLS 4+ */ +#include typedef psa_hash_operation_t my_md5_ctx; @@ -160,10 +136,9 @@ static CURLcode my_md5_init(void *ctx) } static void my_md5_update(void *ctx, - const unsigned char *data, - unsigned int length) + const unsigned char *input, unsigned int len) { - (void)psa_hash_update(ctx, data, length); + (void)psa_hash_update(ctx, input, len); } static void my_md5_final(unsigned char *digest, void *ctx) @@ -172,7 +147,15 @@ static void my_md5_final(unsigned char *digest, void *ctx) (void)psa_hash_finish(ctx, digest, 16, &actual_length); } -#elif defined(AN_APPLE_OS) +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ + defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ + (__MAC_OS_X_VERSION_MIN_REQUIRED < 101500)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ + defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ + (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) +#include /* For Apple operating systems: CommonCrypto has the functions we need. These functions are available on Tiger and later, as well as iOS 2.0 @@ -191,10 +174,9 @@ static CURLcode my_md5_init(void *ctx) } static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int inputLen) + const unsigned char *input, unsigned int len) { - CC_MD5_Update(ctx, input, inputLen); + CC_MD5_Update(ctx, input, len); } static void my_md5_final(unsigned char *digest, void *ctx) @@ -203,6 +185,7 @@ static void my_md5_final(unsigned char *digest, void *ctx) } #elif defined(USE_WIN32_CRYPTO) +#include struct md5_ctx { HCRYPTPROV hCryptProv; @@ -227,11 +210,10 @@ static CURLcode my_md5_init(void *in) } static void my_md5_update(void *in, - const unsigned char *input, - unsigned int inputLen) + const unsigned char *input, unsigned int len) { my_md5_ctx *ctx = in; - CryptHashData(ctx->hHash, (const BYTE *)input, inputLen, 0); + CryptHashData(ctx->hHash, (const BYTE *)input, len, 0); } static void my_md5_final(unsigned char *digest, void *in) @@ -256,17 +238,17 @@ static void my_md5_final(unsigned char *digest, void *in) * MD5 Message-Digest Algorithm (RFC 1321). * * Homepage: - https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 * * Author: * Alexander Peslyak, better known as Solar Designer * * This software was written by Alexander Peslyak in 2001. No copyright is - * claimed, and the software is hereby placed in the public domain. - * In case this attempt to disclaim copyright and place the software in the - * public domain is deemed null and void, then the software is - * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the - * general public under the following terms: + * claimed, and the software is hereby placed in the public domain. In case + * this attempt to disclaim copyright and place the software in the public + * domain is deemed null and void, then the software is Copyright (c) 2001 + * Alexander Peslyak and it is hereby released to the general public under + * the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. @@ -276,27 +258,19 @@ static void my_md5_final(unsigned char *digest, void *in) * (This is a heavily cut-down "BSD license".) */ -/* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int MD5_u32plus; - struct md5_ctx { - MD5_u32plus lo, hi; - MD5_u32plus a, b, c, d; + uint32_t lo, hi; + uint32_t a, b, c, d; unsigned char buffer[64]; - MD5_u32plus block[16]; + uint32_t block[16]; }; typedef struct md5_ctx my_md5_ctx; -static CURLcode my_md5_init(void *ctx); -static void my_md5_update(void *ctx, const unsigned char *data, - unsigned int size); -static void my_md5_final(unsigned char *result, void *ctx); - /* * The basic MD5 functions. * * F and G are optimized compared to their RFC 1321 definitions for - * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * architectures that lack an AND-NOT instruction, like in Colin Plumb's * implementation. */ #define MD5_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) @@ -309,7 +283,7 @@ static void my_md5_final(unsigned char *result, void *ctx); * The MD5 transformation for all four rounds. */ #define MD5_STEP(f, a, b, c, d, x, t, s) \ - (a) += f((b), (c), (d)) + (x) + (t); \ + (a) += f(b, c, d) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b); @@ -317,20 +291,19 @@ static void my_md5_final(unsigned char *result, void *ctx); * SET reads 4 input bytes in little-endian byte order and stores them * in a properly aligned word in host byte order. * - * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * does not work. + * The check for little-endian architectures that tolerate unaligned memory + * accesses is an optimization. Nothing will break if it does not work. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define MD5_SET(n) (*(const MD5_u32plus *)(const void *)&ptr[(n) * 4]) +#define MD5_SET(n) (*(const uint32_t *)(const void *)&ptr[(n) * 4]) #define MD5_GET(n) MD5_SET(n) #else -#define MD5_SET(n) (ctx->block[(n)] = \ - (MD5_u32plus)ptr[(n) * 4] | \ - ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ - ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ - ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) -#define MD5_GET(n) (ctx->block[(n)]) +#define MD5_SET(n) (ctx->block[n] = \ + (uint32_t)ptr[(n) * 4] | \ + ((uint32_t)ptr[((n) * 4) + 1] << 8) | \ + ((uint32_t)ptr[((n) * 4) + 2] << 16) | \ + ((uint32_t)ptr[((n) * 4) + 3] << 24)) +#define MD5_GET(n) ctx->block[n] #endif /* @@ -341,7 +314,7 @@ static const void *my_md5_body(my_md5_ctx *ctx, const void *data, unsigned long size) { const unsigned char *ptr; - MD5_u32plus a, b, c, d; + uint32_t a, b, c, d; ptr = (const unsigned char *)data; @@ -351,7 +324,7 @@ static const void *my_md5_body(my_md5_ctx *ctx, d = ctx->d; do { - MD5_u32plus saved_a, saved_b, saved_c, saved_d; + uint32_t saved_a, saved_b, saved_c, saved_d; saved_a = a; saved_b = b; @@ -460,44 +433,44 @@ static CURLcode my_md5_init(void *in) return CURLE_OK; } -static void my_md5_update(void *in, const unsigned char *data, - unsigned int size) +static void my_md5_update(void *in, + const unsigned char *input, unsigned int len) { - MD5_u32plus saved_lo; + uint32_t saved_lo; unsigned int used; my_md5_ctx *ctx = (my_md5_ctx *)in; saved_lo = ctx->lo; - ctx->lo = (saved_lo + size) & 0x1fffffff; + ctx->lo = (saved_lo + len) & 0x1fffffff; if(ctx->lo < saved_lo) ctx->hi++; - ctx->hi += (MD5_u32plus)size >> 29; + ctx->hi += (uint32_t)len >> 29; used = saved_lo & 0x3f; if(used) { unsigned int available = 64 - used; - if(size < available) { - memcpy(&ctx->buffer[used], data, size); + if(len < available) { + memcpy(&ctx->buffer[used], input, len); return; } - memcpy(&ctx->buffer[used], data, available); - data = (const unsigned char *)data + available; - size -= available; + memcpy(&ctx->buffer[used], input, available); + input = (const unsigned char *)input + available; + len -= available; my_md5_body(ctx, ctx->buffer, 64); } - if(size >= 64) { - data = my_md5_body(ctx, data, size & ~(unsigned long)0x3f); - size &= 0x3f; + if(len >= 64) { + input = my_md5_body(ctx, input, len & ~(unsigned long)0x3f); + len &= 0x3f; } - memcpy(ctx->buffer, data, size); + memcpy(ctx->buffer, input, len); } -static void my_md5_final(unsigned char *result, void *in) +static void my_md5_final(unsigned char *digest, void *in) { unsigned int used, available; my_md5_ctx *ctx = (my_md5_ctx *)in; @@ -529,22 +502,22 @@ static void my_md5_final(unsigned char *result, void *in) my_md5_body(ctx, ctx->buffer, 64); - result[0] = curlx_ultouc((ctx->a) & 0xff); - result[1] = curlx_ultouc((ctx->a >> 8) & 0xff); - result[2] = curlx_ultouc((ctx->a >> 16) & 0xff); - result[3] = curlx_ultouc(ctx->a >> 24); - result[4] = curlx_ultouc((ctx->b) & 0xff); - result[5] = curlx_ultouc((ctx->b >> 8) & 0xff); - result[6] = curlx_ultouc((ctx->b >> 16) & 0xff); - result[7] = curlx_ultouc(ctx->b >> 24); - result[8] = curlx_ultouc((ctx->c) & 0xff); - result[9] = curlx_ultouc((ctx->c >> 8) & 0xff); - result[10] = curlx_ultouc((ctx->c >> 16) & 0xff); - result[11] = curlx_ultouc(ctx->c >> 24); - result[12] = curlx_ultouc((ctx->d) & 0xff); - result[13] = curlx_ultouc((ctx->d >> 8) & 0xff); - result[14] = curlx_ultouc((ctx->d >> 16) & 0xff); - result[15] = curlx_ultouc(ctx->d >> 24); + digest[0] = curlx_ultouc((ctx->a) & 0xff); + digest[1] = curlx_ultouc((ctx->a >> 8) & 0xff); + digest[2] = curlx_ultouc((ctx->a >> 16) & 0xff); + digest[3] = curlx_ultouc(ctx->a >> 24); + digest[4] = curlx_ultouc((ctx->b) & 0xff); + digest[5] = curlx_ultouc((ctx->b >> 8) & 0xff); + digest[6] = curlx_ultouc((ctx->b >> 16) & 0xff); + digest[7] = curlx_ultouc(ctx->b >> 24); + digest[8] = curlx_ultouc((ctx->c) & 0xff); + digest[9] = curlx_ultouc((ctx->c >> 8) & 0xff); + digest[10] = curlx_ultouc((ctx->c >> 16) & 0xff); + digest[11] = curlx_ultouc(ctx->c >> 24); + digest[12] = curlx_ultouc((ctx->d) & 0xff); + digest[13] = curlx_ultouc((ctx->d >> 8) & 0xff); + digest[14] = curlx_ultouc((ctx->d >> 16) & 0xff); + digest[15] = curlx_ultouc(ctx->d >> 24); memset(ctx, 0, sizeof(*ctx)); } @@ -572,8 +545,8 @@ const struct MD5_params Curl_DIGEST_MD5 = { * @unittest: 1601 * Returns CURLE_OK on success. */ -CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input, - const size_t len) +CURLcode Curl_md5it(unsigned char *output, + const unsigned char *input, const size_t len) { CURLcode result; my_md5_ctx ctx; @@ -581,7 +554,7 @@ CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input, result = my_md5_init(&ctx); if(!result) { my_md5_update(&ctx, input, curlx_uztoui(len)); - my_md5_final(outbuffer, &ctx); + my_md5_final(output, &ctx); } return result; } @@ -615,10 +588,9 @@ struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params) } CURLcode Curl_MD5_update(struct MD5_context *context, - const unsigned char *data, - unsigned int len) + const unsigned char *input, unsigned int len) { - (*context->md5_hash->md5_update_func)(context->md5_hashctx, data, len); + (*context->md5_hash->md5_update_func)(context->md5_hashctx, input, len); return CURLE_OK; } diff --git a/vendor/curl/lib/memdebug.c b/vendor/curl/lib/memdebug.c index a99972f..af4fc9d 100644 --- a/vendor/curl/lib/memdebug.c +++ b/vendor/curl/lib/memdebug.c @@ -23,7 +23,7 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef CURLDEBUG +#ifdef CURL_MEMDEBUG #include /* for offsetof() */ @@ -32,7 +32,7 @@ #include "curlx/fopen.h" /* for CURLX_FOPEN_LOW(), CURLX_FREOPEN_LOW() */ #ifdef USE_BACKTRACE -#include "backtrace.h" +#include #endif struct memdebug { @@ -115,6 +115,7 @@ static void curl_dbg_cleanup(void) } #endif } + #ifdef USE_BACKTRACE static void error_bt_callback(void *data, const char *message, int error_number) @@ -218,7 +219,7 @@ void *curl_dbg_malloc(size_t wantedsize, int line, const char *source) /* alloc at least 64 bytes */ size = sizeof(struct memdebug) + wantedsize; - mem = (Curl_cmalloc)(size); + mem = Curl_cmalloc(size); if(mem) { mem->size = wantedsize; } @@ -248,7 +249,7 @@ void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, user_size = wanted_size * wanted_elements; size = sizeof(struct memdebug) + user_size; - mem = (Curl_ccalloc)(1, size); + mem = Curl_ccalloc(1, size); if(mem) mem->size = user_size; @@ -343,7 +344,7 @@ void *curl_dbg_realloc(void *ptr, size_t wantedsize, # pragma warning(pop) #endif - mem = (Curl_crealloc)(mem, size); + mem = Curl_crealloc(mem, size); if(source) curl_dbg_log_locked("MEM %s:%d realloc(%p, %zu) = %p\n", source, line, (void *)ptr, wantedsize, @@ -379,7 +380,7 @@ void curl_dbg_free(void *ptr, int line, const char *source) #endif /* free for real */ - (Curl_cfree)(mem); + Curl_cfree(mem); } } @@ -496,8 +497,7 @@ ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, int line, const char *source) { - /* !checksrc! disable BANNEDFUNC 1 */ - FILE *res = fdopen(filedes, mode); + FILE *res = CURLX_FDOPEN_LOW(filedes, mode); if(source) curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n", source, line, filedes, mode, (void *)res); @@ -574,4 +574,4 @@ void curl_dbg_log(const char *format, ...) curl_dbg_unlock(was_locked); } -#endif /* CURLDEBUG */ +#endif /* CURL_MEMDEBUG */ diff --git a/vendor/curl/lib/mime.c b/vendor/curl/lib/mime.c index 37b50de..37ea514 100644 --- a/vendor/curl/lib/mime.c +++ b/vendor/curl/lib/mime.c @@ -30,59 +30,27 @@ struct Curl_easy; #include "sendf.h" #include "curl_trc.h" #include "transfer.h" -#include "strdup.h" +#include "curlx/strdup.h" +#include "curlx/basename.h" #include "curlx/strcopy.h" #include "curlx/fopen.h" #include "curlx/base64.h" -#if !defined(CURL_DISABLE_MIME) && \ - (!defined(CURL_DISABLE_HTTP) || \ - !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP)) - -#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) -#include -#endif +#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \ + !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP)) #include "rand.h" #include "slist.h" #include "curlx/dynbuf.h" -#ifdef _WIN32 -# ifndef R_OK -# define R_OK 4 -# endif -#endif - #define READ_ERROR ((size_t)-1) #define STOP_FILLING ((size_t)-2) static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, void *instream, bool *hasread); - -/* Encoders. */ -static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_nop_size(curl_mimepart *part); -static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_base64_size(curl_mimepart *part); -static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_qp_size(curl_mimepart *part); static curl_off_t mime_size(curl_mimepart *part); -static const struct mime_encoder encoders[] = { - { "binary", encoder_nop_read, encoder_nop_size }, - { "8bit", encoder_nop_read, encoder_nop_size }, - { "7bit", encoder_7bit_read, encoder_nop_size }, - { "base64", encoder_base64_read, encoder_base64_size }, - { "quoted-printable", encoder_qp_read, encoder_qp_size }, - { ZERO_NULL, ZERO_NULL, ZERO_NULL } -}; - /* Quoted-printable character class table. * * We cannot rely on ctype functions since quoted-printable input data @@ -123,7 +91,7 @@ static const char aschex[] = "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x41\x42\x43\x44\x45\x46"; #ifndef __VMS -#define filesize(name, stat_data) (stat_data.st_size) +#define filesize(name, stat_data) stat_data.st_size #define fopen_read curlx_fopen #else @@ -139,7 +107,7 @@ static const char aschex[] = * and CD/DVD images should be either a STREAM_LF format or a fixed format. * */ -curl_off_t VmsRealFileSize(const char *name, const struct_stat *stat_buf) +curl_off_t VmsRealFileSize(const char *name, const curlx_struct_stat *stat_buf) { char buffer[8192]; curl_off_t count; @@ -168,7 +136,8 @@ curl_off_t VmsRealFileSize(const char *name, const struct_stat *stat_buf) * if not to call a routine to get the correct size. * */ -static curl_off_t VmsSpecialSize(const char *name, const struct_stat *stat_buf) +static curl_off_t VmsSpecialSize(const char *name, + const curlx_struct_stat *stat_buf) { switch(stat_buf->st_fab_rfm) { case FAB$C_VAR: @@ -192,7 +161,7 @@ static curl_off_t VmsSpecialSize(const char *name, const struct_stat *stat_buf) */ static FILE *vmsfopenread(const char *file, const char *mode) { - struct_stat statbuf; + curlx_struct_stat statbuf; int result; result = curlx_stat(file, &statbuf); @@ -209,56 +178,7 @@ static FILE *vmsfopenread(const char *file, const char *mode) } #define fopen_read vmsfopenread -#endif - -#ifndef HAVE_BASENAME -/* - (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 - Edition) - - The basename() function shall take the pathname pointed to by path and - return a pointer to the final component of the pathname, deleting any - trailing '/' characters. - - If the string pointed to by path consists entirely of the '/' character, - basename() shall return a pointer to the string "/". If the string pointed - to by path is exactly "//", it is implementation-defined whether '/' or "//" - is returned. - - If path is a null pointer or points to an empty string, basename() shall - return a pointer to the string ".". - - The basename() function may modify the string pointed to by path, and may - return a pointer to static storage that may then be overwritten by a - subsequent call to basename(). - - The basename() function need not be reentrant. A function that is not - required to be reentrant is not required to be thread-safe. - -*/ -static char *Curl_basename(char *path) -{ - /* Ignore all the details above for now and make a quick and simple - implementation here */ - char *s1; - char *s2; - - s1 = strrchr(path, '/'); - s2 = strrchr(path, '\\'); - - if(s1 && s2) { - path = (s1 > s2 ? s1 : s2) + 1; - } - else if(s1) - path = s1 + 1; - else if(s2) - path = s2 + 1; - - return path; -} - -#define basename(x) Curl_basename(x) -#endif +#endif /* !__VMS */ /* Set readback state. */ static void mimesetstate(struct mime_state *state, @@ -348,7 +268,7 @@ static char *strippath(const char *fullfile) the buffer it works on */ if(!filename) return NULL; - base = curlx_strdup(basename(filename)); + base = curlx_strdup(curlx_basename(filename)); curlx_free(filename); /* free temporary buffer */ @@ -363,7 +283,7 @@ static void cleanup_encoder_state(struct mime_encoder_state *p) p->bufend = 0; } -/* Dummy encoder. This is used for 8bit and binary content encodings. */ +/* Dummy encoder. This is used for 8-bit and binary content encodings. */ static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, struct curl_mimepart *part) { @@ -390,7 +310,7 @@ static curl_off_t encoder_nop_size(curl_mimepart *part) return part->datasize; } -/* 7bit encoder: the encoder is just a data validity check. */ +/* 7-bit encoder: the encoder is a data validity check. */ static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, curl_mimepart *part) { @@ -503,10 +423,10 @@ static curl_off_t encoder_base64_size(curl_mimepart *part) return size; /* Unknown size or no data. */ /* Compute base64 character count. */ - size = 4 * (1 + (size - 1) / 3); + size = 4 * (1 + ((size - 1) / 3)); /* Effective character count must include CRLFs. */ - return size + 2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH); + return size + (2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH)); } /* Quoted-printable lookahead. @@ -544,7 +464,7 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, while(st->bufbeg < st->bufend) { size_t len = 1; size_t consumed = 1; - int i = st->buf[st->bufbeg]; + int i = (unsigned char)st->buf[st->bufbeg]; buf[0] = (char)i; buf[1] = aschex[(i >> 4) & 0xF]; buf[2] = aschex[i & 0xF]; @@ -602,7 +522,7 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, } } if(softlinebreak) { - curlx_strcopy(buf, sizeof(buf), "\x3D\x0D\x0A", 3); /* "=\r\n" */ + curlx_strcopy(buf, sizeof(buf), STRCONST("\x3D\x0D\x0A")); /* =\r\n */ len = 3; consumed = 0; } @@ -1285,7 +1205,7 @@ curl_mime *curl_mime_init(void *easy) /* Initialize a mime part. */ void Curl_mime_initpart(curl_mimepart *part) { - memset((char *)part, 0, sizeof(*part)); + memset(part, 0, sizeof(*part)); part->lastreadstatus = 1; /* Successful read status. */ mimesetstate(&part->state, MIMESTATE_BEGIN, NULL); } @@ -1350,18 +1270,18 @@ CURLcode curl_mime_filename(curl_mimepart *part, const char *filename) } /* Set mime part content from memory data. */ -CURLcode curl_mime_data(curl_mimepart *part, const char *ptr, size_t datasize) +CURLcode curl_mime_data(curl_mimepart *part, const char *data, size_t datasize) { if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; cleanup_part_content(part); - if(ptr) { + if(data) { if(datasize == CURL_ZERO_TERMINATED) - datasize = strlen(ptr); + datasize = strlen(data); - part->data = Curl_memdup0(ptr, datasize); + part->data = curlx_memdup0(data, datasize); if(!part->data) return CURLE_OUT_OF_MEMORY; @@ -1388,7 +1308,7 @@ CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename) if(filename) { char *base; - struct_stat sbuf; + curlx_struct_stat sbuf; if(curlx_stat(filename, &sbuf)) result = CURLE_READ_ERROR; @@ -1441,6 +1361,15 @@ CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype) return CURLE_OK; } +static const struct mime_encoder encoders[] = { + { "binary", encoder_nop_read, encoder_nop_size }, + { "8bit", encoder_nop_read, encoder_nop_size }, + { "7bit", encoder_7bit_read, encoder_nop_size }, + { "base64", encoder_base64_read, encoder_base64_size }, + { "quoted-printable", encoder_qp_read, encoder_qp_size }, + { ZERO_NULL, ZERO_NULL, ZERO_NULL } +}; + /* Set mime data transfer encoder. */ CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) { @@ -2057,7 +1986,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data, if(ctx->total_len >= 0) ctx->seen_eos = (ctx->read_len >= ctx->total_len); *pnread = nread; - *peos = ctx->seen_eos; + *peos = (bool)ctx->seen_eos; break; } @@ -2194,7 +2123,8 @@ CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part) } #else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP || - !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */ + !CURL_DISABLE_SMTP || + !CURL_DISABLE_IMAP) */ /* Mime not compiled in: define stubs for externally-referenced functions. */ curl_mime *curl_mime_init(CURL *easy) diff --git a/vendor/curl/lib/mime.h b/vendor/curl/lib/mime.h index 81080b4..e84f040 100644 --- a/vendor/curl/lib/mime.h +++ b/vendor/curl/lib/mime.h @@ -160,15 +160,14 @@ const char *Curl_mime_contenttype(const char *filename); */ CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part); -#else -/* if disabled */ +#else /* if disabled */ #define Curl_mime_initpart(x) #define Curl_mime_cleanpart(x) #define Curl_mime_duppart(x, y, z) CURLE_OK /* Nothing to duplicate. Succeed */ #define Curl_mime_set_subparts(a, b, c) CURLE_NOT_BUILT_IN #define Curl_mime_prepare_headers(a, b, c, d, e) CURLE_NOT_BUILT_IN #define Curl_mime_read NULL -#define Curl_creader_set_mime(x, y) ((void)x, CURLE_NOT_BUILT_IN) +#define Curl_creader_set_mime(x, y) ((void)(x), CURLE_NOT_BUILT_IN) #endif #endif /* HEADER_CURL_MIME_H */ diff --git a/vendor/curl/lib/mprintf.c b/vendor/curl/lib/mprintf.c index 50ede21..c6a4a49 100644 --- a/vendor/curl/lib/mprintf.c +++ b/vendor/curl/lib/mprintf.c @@ -26,67 +26,41 @@ #include "curlx/dynbuf.h" #include "curl_printf.h" #include "curlx/strparse.h" - -#ifdef HAVE_LONGLONG -# define LONG_LONG_TYPE long long -# define HAVE_LONG_LONG_TYPE -#elif defined(_MSC_VER) -# define LONG_LONG_TYPE __int64 -# define HAVE_LONG_LONG_TYPE -#else -# undef LONG_LONG_TYPE -# undef HAVE_LONG_LONG_TYPE -#endif - -/* - * Max integer data types that mprintf.c is capable - */ - -#ifdef HAVE_LONG_LONG_TYPE -# define mp_intmax_t LONG_LONG_TYPE -# define mp_uintmax_t unsigned LONG_LONG_TYPE -#else -# define mp_intmax_t long -# define mp_uintmax_t unsigned long -#endif +#include "curlx/snprintf.h" /* for curlx_win32_snprintf() */ #define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should fit negative DBL_MAX (317 letters) */ #define MAX_PARAMETERS 128 /* number of input arguments */ #define MAX_SEGMENTS 128 /* number of output segments */ -#ifdef __AMIGA__ -# undef FORMAT_INT -#endif - -/* Lower-case digits. */ +/* Lower-case digits. */ const unsigned char Curl_ldigits[] = "0123456789abcdef"; -/* Upper-case digits. */ +/* Upper-case digits. */ const unsigned char Curl_udigits[] = "0123456789ABCDEF"; #define OUTCHAR(x) \ do { \ - if(stream((unsigned char)x, userp)) \ + if(stream((unsigned char)(x), userp)) \ return TRUE; \ (*donep)++; \ } while(0) /* Data type to read from the arglist */ typedef enum { - FORMAT_STRING, - FORMAT_PTR, - FORMAT_INTPTR, - FORMAT_INT, - FORMAT_LONG, - FORMAT_LONGLONG, - FORMAT_INTU, - FORMAT_LONGU, - FORMAT_LONGLONGU, - FORMAT_DOUBLE, - FORMAT_LONGDOUBLE, - FORMAT_WIDTH, - FORMAT_PRECISION + MTYPE_STRING, + MTYPE_PTR, + MTYPE_INTPTR, + MTYPE_INT, + MTYPE_LONG, + MTYPE_LONGLONG, + MTYPE_INTU, + MTYPE_LONGU, + MTYPE_LONGLONGU, + MTYPE_DOUBLE, + MTYPE_LONGDOUBLE, + MTYPE_WIDTH, + MTYPE_PRECISION } FormatType; /* conversion and display flags */ @@ -128,8 +102,8 @@ struct va_input { union { const char *str; void *ptr; - mp_intmax_t nums; /* signed */ - mp_uintmax_t numu; /* unsigned */ + int64_t nums; /* signed */ + uint64_t numu; /* unsigned */ double dnum; } val; }; @@ -158,9 +132,9 @@ struct asprintf { }; /* the provided input number is 1-based but this returns the number 0-based. - - returns -1 if no valid number was provided. -*/ + * + * returns -1 if no valid number was provided. + */ static int dollarstring(const char *p, const char **end) { curl_off_t num; @@ -172,7 +146,7 @@ static int dollarstring(const char *p, const char **end) } #define is_arg_used(x, y) ((x)[(y) / 8] & (1 << ((y) & 7))) -#define mark_arg_used(x, y) ((x)[y / 8] |= (unsigned char)(1 << ((y) & 7))) +#define mark_arg_used(x, y) ((x)[(y) / 8] |= (unsigned char)(1 << ((y) & 7))) /* * Parse the format string. @@ -250,7 +224,7 @@ static int parsefmt(const char *format, /* illegal combo */ return PFMT_DOLLAR; - /* we got no positional, just get the next arg */ + /* we got no positional, get the next arg */ param = -1; use_dollar = DOLLAR_NOPE; } @@ -405,80 +379,80 @@ static int parsefmt(const char *format, flags |= FLAGS_ALT; FALLTHROUGH(); case 's': - type = FORMAT_STRING; + type = MTYPE_STRING; break; case 'n': - type = FORMAT_INTPTR; + type = MTYPE_INTPTR; break; case 'p': - type = FORMAT_PTR; + type = MTYPE_PTR; break; case 'd': case 'i': if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONG; + type = MTYPE_LONGLONG; else if(flags & FLAGS_LONG) - type = FORMAT_LONG; + type = MTYPE_LONG; else - type = FORMAT_INT; + type = MTYPE_INT; break; case 'u': if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONGU; + type = MTYPE_LONGLONGU; else if(flags & FLAGS_LONG) - type = FORMAT_LONGU; + type = MTYPE_LONGU; else - type = FORMAT_INTU; + type = MTYPE_INTU; flags |= FLAGS_UNSIGNED; break; case 'o': if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONGU; + type = MTYPE_LONGLONGU; else if(flags & FLAGS_LONG) - type = FORMAT_LONGU; + type = MTYPE_LONGU; else - type = FORMAT_INTU; + type = MTYPE_INTU; flags |= FLAGS_OCTAL | FLAGS_UNSIGNED; break; case 'x': if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONGU; + type = MTYPE_LONGLONGU; else if(flags & FLAGS_LONG) - type = FORMAT_LONGU; + type = MTYPE_LONGU; else - type = FORMAT_INTU; + type = MTYPE_INTU; flags |= FLAGS_HEX | FLAGS_UNSIGNED; break; case 'X': if(flags & FLAGS_LONGLONG) - type = FORMAT_LONGLONGU; + type = MTYPE_LONGLONGU; else if(flags & FLAGS_LONG) - type = FORMAT_LONGU; + type = MTYPE_LONGU; else - type = FORMAT_INTU; + type = MTYPE_INTU; flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED; break; case 'c': - type = FORMAT_INT; + type = MTYPE_INT; flags |= FLAGS_CHAR; break; case 'f': - type = FORMAT_DOUBLE; + type = MTYPE_DOUBLE; break; case 'e': - type = FORMAT_DOUBLE; + type = MTYPE_DOUBLE; flags |= FLAGS_FLOATE; break; case 'E': - type = FORMAT_DOUBLE; + type = MTYPE_DOUBLE; flags |= FLAGS_FLOATE | FLAGS_UPPER; break; case 'g': - type = FORMAT_DOUBLE; + type = MTYPE_DOUBLE; flags |= FLAGS_FLOATG; break; case 'G': - type = FORMAT_DOUBLE; + type = MTYPE_DOUBLE; flags |= FLAGS_FLOATG | FLAGS_UPPER; break; default: @@ -499,7 +473,7 @@ static int parsefmt(const char *format, if(width >= max_param) max_param = width; - in[width].type = FORMAT_WIDTH; + in[width].type = MTYPE_WIDTH; /* mark as used */ mark_arg_used(usedinput, width); } @@ -517,7 +491,7 @@ static int parsefmt(const char *format, if(precision >= max_param) max_param = precision; - in[precision].type = FORMAT_PRECISION; + in[precision].type = MTYPE_PRECISION; mark_arg_used(usedinput, precision); } @@ -572,42 +546,42 @@ static int parsefmt(const char *format, /* based on the type, read the correct argument */ switch(iptr->type) { - case FORMAT_STRING: + case MTYPE_STRING: iptr->val.str = va_arg(arglist, const char *); break; - case FORMAT_INTPTR: - case FORMAT_PTR: + case MTYPE_INTPTR: + case MTYPE_PTR: iptr->val.ptr = va_arg(arglist, void *); break; - case FORMAT_LONGLONGU: - iptr->val.numu = (mp_uintmax_t)va_arg(arglist, mp_uintmax_t); + case MTYPE_LONGLONGU: + iptr->val.numu = va_arg(arglist, uint64_t); break; - case FORMAT_LONGLONG: - iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t); + case MTYPE_LONGLONG: + iptr->val.nums = va_arg(arglist, int64_t); break; - case FORMAT_LONGU: - iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned long); + case MTYPE_LONGU: + iptr->val.numu = va_arg(arglist, unsigned long); break; - case FORMAT_LONG: - iptr->val.nums = (mp_intmax_t)va_arg(arglist, long); + case MTYPE_LONG: + iptr->val.nums = va_arg(arglist, long); break; - case FORMAT_INTU: - iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned int); + case MTYPE_INTU: + iptr->val.numu = va_arg(arglist, unsigned int); break; - case FORMAT_INT: - case FORMAT_WIDTH: - case FORMAT_PRECISION: - iptr->val.nums = (mp_intmax_t)va_arg(arglist, int); + case MTYPE_INT: + case MTYPE_WIDTH: + case MTYPE_PRECISION: + iptr->val.nums = va_arg(arglist, int); break; - case FORMAT_DOUBLE: + case MTYPE_DOUBLE: iptr->val.dnum = va_arg(arglist, double); break; @@ -623,8 +597,8 @@ static int parsefmt(const char *format, } struct mproperty { - int width; /* Width of a field. */ - int prec; /* Precision of a field. */ + int width; /* Width of a field. */ + int prec; /* Precision of a field. */ unsigned int flags; }; @@ -634,9 +608,9 @@ static bool out_double(void *userp, double dnum, char *work, int *donep) { - char formatbuf[32] = "%"; - char *fptr = &formatbuf[1]; - size_t left = sizeof(formatbuf) - strlen(formatbuf); + char fmt[32] = "%"; + char *fptr = &fmt[1]; + size_t left = sizeof(fmt) - strlen(fmt); int flags = p->flags; int width = p->width; int prec = p->prec; @@ -696,26 +670,25 @@ static bool out_double(void *userp, *fptr = 0; /* and a final null-termination */ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#endif /* NOTE NOTE NOTE!! Not all sprintf implementations return number of output characters */ -#ifdef HAVE_SNPRINTF +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef _WIN32 + curlx_win32_snprintf(work, BUFFSIZE, fmt, dnum); +#elif defined(HAVE_SNPRINTF) + /* !checksrc! disable BANNEDFUNC 1 */ /* !checksrc! disable LONGLINE */ /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ - (snprintf)(work, BUFFSIZE, formatbuf, dnum); -#ifdef _WIN32 - /* Old versions of the Windows CRT do not terminate the snprintf output - buffer if it reaches the max size so we do that here. */ - work[BUFFSIZE - 1] = 0; -#endif + snprintf(work, BUFFSIZE, fmt, dnum); #else - (sprintf)(work, formatbuf, dnum); + /* float and double outputs do not work without snprintf support */ + work[0] = 0; #endif -#ifdef __clang__ -#pragma clang diagnostic pop +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop #endif DEBUGASSERT(strlen(work) < BUFFSIZE); while(*work) { @@ -729,8 +702,8 @@ static bool out_double(void *userp, static bool out_number(void *userp, int (*stream)(unsigned char, void *), struct mproperty *p, - mp_uintmax_t num, - mp_intmax_t nums, + uint64_t num, + int64_t nums, char *work, int *donep) { const unsigned char *digits = Curl_ldigits; @@ -748,7 +721,7 @@ static bool out_number(void *userp, char *w; if(flags & FLAGS_CHAR) { - /* Character. */ + /* Character. */ if(!(flags & FLAGS_LEFT)) while(--width > 0) OUTCHAR(' '); @@ -772,23 +745,23 @@ static bool out_number(void *userp, ; else { - /* Decimal integer. */ + /* Decimal integer. */ is_neg = (nums < 0); if(is_neg) { /* signed_num might fail to hold absolute negative minimum by 1 */ - mp_intmax_t signed_num; /* Used to convert negative in positive. */ - signed_num = nums + (mp_intmax_t)1; + int64_t signed_num; /* Used to convert negative in positive. */ + signed_num = nums + (int64_t)1; signed_num = -signed_num; - num = (mp_uintmax_t)signed_num; - num += (mp_uintmax_t)1; + num = (uint64_t)signed_num; + num += (uint64_t)1; } } - /* Supply a default precision if none was given. */ + /* Supply a default precision if none was given. */ if(prec == -1) prec = 1; - /* Put the number in WORK. */ + /* Put the number in WORK. */ w = workend; DEBUGASSERT(base <= 16); switch(base) { @@ -848,7 +821,7 @@ static bool out_number(void *userp, while(width-- > 0) OUTCHAR('0'); - /* Write the number. */ + /* Write the number. */ while(++w <= workend) { OUTCHAR(*w); } @@ -874,7 +847,7 @@ static bool out_string(void *userp, size_t len; if(!str) { - /* Write null string if there is space. */ + /* Write null string if there is space. */ if(prec == -1 || prec >= (int)sizeof(nilstr) - 1) { str = nilstr; len = sizeof(nilstr) - 1; @@ -921,17 +894,17 @@ static bool out_pointer(void *userp, char *work, int *donep) { - /* Generic pointer. */ + /* Generic pointer. */ if(ptr) { size_t num = (size_t)ptr; - /* If the pointer is not NULL, write it as a %#x spec. */ + /* If the pointer is not NULL, write it as a %#x spec. */ p->flags |= FLAGS_HEX | FLAGS_ALT; if(out_number(userp, stream, p, num, 0, work, donep)) return TRUE; } else { - /* Write "(nil)" for a nil pointer. */ + /* Write "(nil)" for a nil pointer. */ const char *point; int width = p->width; int flags = p->flags; @@ -965,14 +938,14 @@ static bool out_pointer(void *userp, * All output is sent to the 'stream()' callback, one byte at a time. */ -static int formatf(void *userp, /* untouched by format(), just sent to the +static int formatf(void *userp, /* untouched by format(), sent to the stream() function in the second argument */ /* function pointer called for each output character */ int (*stream)(unsigned char, void *), const char *format, /* %-formatted string */ va_list ap_save) /* list of parameters */ { - int done = 0; /* number of characters written */ + int done = 0; /* number of characters written */ int i; int ocount = 0; /* number of output segments */ int icount = 0; /* number of input arguments */ @@ -999,7 +972,7 @@ static int formatf(void *userp, /* untouched by format(), just sent to the done++; } if(optr->flags & FLAGS_SUBSTR) - /* this is just a substring */ + /* this is a substring */ continue; } @@ -1036,48 +1009,46 @@ static int formatf(void *userp, /* untouched by format(), just sent to the p.prec = -1; switch(iptr->type) { - case FORMAT_INTU: - case FORMAT_LONGU: - case FORMAT_LONGLONGU: + case MTYPE_INTU: + case MTYPE_LONGU: + case MTYPE_LONGLONGU: p.flags |= FLAGS_UNSIGNED; if(out_number(userp, stream, &p, iptr->val.numu, 0, work, &done)) return done; break; - case FORMAT_INT: - case FORMAT_LONG: - case FORMAT_LONGLONG: + case MTYPE_INT: + case MTYPE_LONG: + case MTYPE_LONGLONG: if(out_number(userp, stream, &p, iptr->val.numu, iptr->val.nums, work, &done)) return done; break; - case FORMAT_STRING: + case MTYPE_STRING: if(out_string(userp, stream, &p, iptr->val.str, &done)) return done; break; - case FORMAT_PTR: + case MTYPE_PTR: if(out_pointer(userp, stream, &p, iptr->val.ptr, work, &done)) return done; break; - case FORMAT_DOUBLE: + case MTYPE_DOUBLE: if(out_double(userp, stream, &p, iptr->val.dnum, work, &done)) return done; break; - case FORMAT_INTPTR: - /* Answer the count of characters written. */ -#ifdef HAVE_LONG_LONG_TYPE + case MTYPE_INTPTR: + /* Answer the count of characters written. */ if(p.flags & FLAGS_LONGLONG) - *(LONG_LONG_TYPE *)iptr->val.ptr = (LONG_LONG_TYPE)done; + *(int64_t *)iptr->val.ptr = (int64_t)done; else -#endif if(p.flags & FLAGS_LONG) *(long *)iptr->val.ptr = (long)done; else if(!(p.flags & FLAGS_SHORT)) - *(int *)iptr->val.ptr = (int)done; + *(int *)iptr->val.ptr = done; else *(short *)iptr->val.ptr = (short)done; break; diff --git a/vendor/curl/lib/mqtt.c b/vendor/curl/lib/mqtt.c index 2ba1ca7..e591cfc 100644 --- a/vendor/curl/lib/mqtt.c +++ b/vendor/curl/lib/mqtt.c @@ -23,10 +23,10 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_MQTT -#include "urldata.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" @@ -36,6 +36,8 @@ #include "url.h" #include "escape.h" #include "rand.h" +#include "cfilters.h" +#include "connect.h" /* first byte is command. second byte is for flags. */ @@ -89,48 +91,6 @@ struct MQTT { BIT(pingsent); /* 1 while we wait for ping response */ }; -/* - * Forward declarations. - */ - -static CURLcode mqtt_do(struct Curl_easy *data, bool *done); -static CURLcode mqtt_done(struct Curl_easy *data, - CURLcode status, bool premature); -static CURLcode mqtt_doing(struct Curl_easy *data, bool *done); -static CURLcode mqtt_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode mqtt_setup_conn(struct Curl_easy *data, - struct connectdata *conn); - -/* - * MQTT protocol handler. - */ - -const struct Curl_handler Curl_handler_mqtt = { - "mqtt", /* scheme */ - mqtt_setup_conn, /* setup_connection */ - mqtt_do, /* do_it */ - mqtt_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - mqtt_doing, /* doing */ - ZERO_NULL, /* proto_pollset */ - mqtt_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_MQTT, /* defport */ - CURLPROTO_MQTT, /* protocol */ - CURLPROTO_MQTT, /* family */ - PROTOPT_NONE /* flags */ -}; - static void mqtt_easy_dtor(void *key, size_t klen, void *entry) { struct MQTT *mq = entry; @@ -419,6 +379,8 @@ static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes) result = Curl_xfer_recv(data, (char *)readbuf, nbytes - rlen, &nread); if(result) return result; + if(!nread) /* EOF */ + return CURLE_RECV_ERROR; if(curlx_dyn_addn(&mq->recvbuf, readbuf, nread)) return CURLE_OUT_OF_MEMORY; rlen = curlx_dyn_len(&mq->recvbuf); @@ -443,15 +405,20 @@ static CURLcode mqtt_verify_connack(struct Curl_easy *data) { struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY); CURLcode result; - char *ptr; + const char *ptr; DEBUGASSERT(mq); if(!mq) return CURLE_FAILED_INIT; + if(mq->remaining_length != 2) { + failf(data, "CONNACK expected Remaining Length 2, got %zu", + mq->remaining_length); + return CURLE_WEIRD_SERVER_REPLY; + } result = mqtt_recv_atleast(data, MQTT_CONNACK_LEN); if(result) - goto fail; + return result; /* verify CONNACK */ DEBUGASSERT(curlx_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN); @@ -462,18 +429,16 @@ static CURLcode mqtt_verify_connack(struct Curl_easy *data) failf(data, "Expected %02x%02x but got %02x%02x", 0x00, 0x00, ptr[0], ptr[1]); curlx_dyn_reset(&mq->recvbuf); - result = CURLE_WEIRD_SERVER_REPLY; - goto fail; + return CURLE_WEIRD_SERVER_REPLY; } mqtt_recv_consume(data, MQTT_CONNACK_LEN); -fail: - return result; + return CURLE_OK; } static CURLcode mqtt_get_topic(struct Curl_easy *data, char **topic, size_t *topiclen) { - char *path = data->state.up.path; + const char *path = data->state.up.path; CURLcode result = CURLE_URL_MALFORMAT; if(strlen(path) > 1) { result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA); @@ -546,11 +511,17 @@ static CURLcode mqtt_verify_suback(struct Curl_easy *data) struct connectdata *conn = data->conn; struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN); CURLcode result; - char *ptr; + const char *ptr; if(!mqtt || !mq) return CURLE_FAILED_INIT; + if(mq->remaining_length != 3) { + failf(data, "SUBACK expected Remaining Length 3, got %zu", + mq->remaining_length); + return CURLE_WEIRD_SERVER_REPLY; + } + result = mqtt_recv_atleast(data, MQTT_SUBACK_LEN); if(result) goto fail; @@ -604,7 +575,7 @@ static CURLcode mqtt_publish(struct Curl_easy *data) remaininglength = payloadlen + 2 + topiclen; encodelen = mqtt_encode_len(encodedbytes, remaininglength); - if(MAX_MQTT_MESSAGE_SIZE - remaininglength - 1 < encodelen) { + if(remaininglength > (MAX_MQTT_MESSAGE_SIZE - encodelen - 1)) { result = CURLE_TOO_LARGE; goto fail; } @@ -635,7 +606,8 @@ static CURLcode mqtt_publish(struct Curl_easy *data) } /* return 0 on success, non-zero on error */ -static int mqtt_decode_len(size_t *lenp, unsigned char *buf, size_t buflen) +static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, + size_t buflen) { size_t len = 0; size_t mult = 1; @@ -654,7 +626,7 @@ static int mqtt_decode_len(size_t *lenp, unsigned char *buf, size_t buflen) return 0; } -#ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) static const char *statenames[] = { "MQTT_FIRST", "MQTT_REMAINING_LENGTH", @@ -755,7 +727,7 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) rest = sizeof(buffer); result = Curl_xfer_recv(data, buffer, rest, &nread); if(result) { - if(CURLE_AGAIN == result) { + if(result == CURLE_AGAIN) { infof(data, "EEEE AAAAGAIN"); } goto end; @@ -976,4 +948,94 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) return result; } +#ifdef USE_SSL + +static CURLcode mqtts_connecting(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + CURLcode result; + + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); + if(result) + connclose(conn, "Failed TLS connection"); + return result; +} + +/* + * MQTTS protocol. + */ +static const struct Curl_protocol Curl_protocol_mqtts = { + mqtt_setup_conn, /* setup_connection */ + mqtt_do, /* do_it */ + mqtt_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + mqtts_connecting, /* connecting */ + mqtt_doing, /* doing */ + ZERO_NULL, /* proto_pollset */ + mqtt_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif + +/* + * MQTT protocol. + */ +static const struct Curl_protocol Curl_protocol_mqtt = { + mqtt_setup_conn, /* setup_connection */ + mqtt_do, /* do_it */ + mqtt_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + mqtt_doing, /* doing */ + ZERO_NULL, /* proto_pollset */ + mqtt_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_MQTT */ + +const struct Curl_scheme Curl_scheme_mqtts = { + "mqtts", /* scheme */ +#if defined(CURL_DISABLE_MQTT) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_mqtts, +#endif + CURLPROTO_MQTTS, /* protocol */ + CURLPROTO_MQTT, /* family */ + PROTOPT_SSL, /* flags */ + PORT_MQTTS, /* defport */ +}; + +/* + * MQTT protocol. + */ +const struct Curl_scheme Curl_scheme_mqtt = { + "mqtt", /* scheme */ +#ifdef CURL_DISABLE_MQTT + ZERO_NULL, +#else + &Curl_protocol_mqtt, +#endif + CURLPROTO_MQTT, /* protocol */ + CURLPROTO_MQTT, /* family */ + PROTOPT_NONE, /* flags */ + PORT_MQTT, /* defport */ +}; diff --git a/vendor/curl/lib/mqtt.h b/vendor/curl/lib/mqtt.h index 3e45815..67d1df4 100644 --- a/vendor/curl/lib/mqtt.h +++ b/vendor/curl/lib/mqtt.h @@ -23,8 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef CURL_DISABLE_MQTT -extern const struct Curl_handler Curl_handler_mqtt; -#endif +extern const struct Curl_scheme Curl_scheme_mqtt; +extern const struct Curl_scheme Curl_scheme_mqtts; #endif /* HEADER_CURL_MQTT_H */ diff --git a/vendor/curl/lib/multi.c b/vendor/curl/lib/multi.c index 7cfd053..6133736 100644 --- a/vendor/curl/lib/multi.c +++ b/vendor/curl/lib/multi.c @@ -160,6 +160,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state return; #ifdef DEBUGBUILD + NOVERBOSE((void)lineno); CURL_TRC_M(data, "-> [%s] (line %d)", CURL_MSTATE_NAME(state), lineno); #else CURL_TRC_M(data, "-> [%s]", CURL_MSTATE_NAME(state)); @@ -294,12 +295,17 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, if(multi->wsa_event == WSA_INVALID_EVENT) goto error; #elif defined(ENABLE_WAKEUP) - if(wakeup_create(multi->wakeup_pair, TRUE) < 0) { + if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) { multi->wakeup_pair[0] = CURL_SOCKET_BAD; multi->wakeup_pair[1] = CURL_SOCKET_BAD; } #endif +#ifdef USE_IPV6 + if(Curl_probeipv6(multi)) + goto error; +#endif + return multi; error: @@ -337,7 +343,7 @@ CURLM *curl_multi_init(void) CURL_TLS_SESSION_SIZE); } -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) { if(!multi->warned) { @@ -351,6 +357,11 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) #define multi_warn_debug(x, y) Curl_nop_stmt #endif +bool Curl_is_connecting(struct Curl_easy *data) +{ + return data->mstate < MSTATE_DO; +} + static CURLMcode multi_xfers_add(struct Curl_multi *multi, struct Curl_easy *data) { @@ -363,8 +374,8 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, if(capacity < max_capacity) { /* We want `multi->xfers` to have "sufficient" free rows, so that we do - * have to reuse the `mid` from a just removed easy right away. - * Since uint_tbl and uint_bset are quite memory efficient, + * have to reuse the `mid` from a removed easy right away. + * Since uint_tbl and uint_bset are memory efficient, * regard less than 25% free as insufficient. * (for low capacities, e.g. multi_easy, 4 or less). */ uint32_t used = Curl_uint32_tbl_count(&multi->xfers); @@ -569,8 +580,8 @@ static bool multi_conn_should_close(struct connectdata *conn, /* Unless this connection is for a "connect-only" transfer, it * needs to be closed if the protocol handler does not support reuse. */ - if(!data->set.connect_only && conn->handler && - !(conn->handler->flags & PROTOPT_CONN_REUSE)) + if(!data->set.connect_only && conn->scheme && + !(conn->scheme->flags & PROTOPT_CONN_REUSE)) return TRUE; /* if premature is TRUE, it means this connection was said to be DONE before @@ -589,7 +600,7 @@ static void multi_done_locked(struct connectdata *conn, void *userdata) { struct multi_done_ctx *mdctx = userdata; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE const char *host = #ifndef CURL_DISABLE_PROXY conn->bits.socksproxy ? @@ -604,7 +615,7 @@ static void multi_done_locked(struct connectdata *conn, #endif conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port; -#endif +#endif /* CURLVERBOSE */ Curl_detach_connection(data); @@ -616,41 +627,35 @@ static void multi_done_locked(struct connectdata *conn, return; } - data->state.done = TRUE; /* called just now! */ + data->state.done = TRUE; /* called now! */ data->state.recent_conn_id = conn->connection_id; Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */ Curl_resolv_unlink(data, &data->state.dns[1]); Curl_dnscache_prune(data); - if(multi_conn_should_close(conn, data, mdctx->premature)) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) { CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%d, " "forbid=%d, close=%d, premature=%d, conn_multiplex=%d", conn->connection_id, host, port, data->set.reuse_forbid, conn->bits.close, mdctx->premature, Curl_conn_is_multiplex(conn, FIRSTSOCKET)); -#endif connclose(conn, "disconnecting"); - Curl_conn_terminate(data, conn, mdctx->premature); + Curl_conn_terminate(data, conn, (bool)mdctx->premature); } else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s:%d was shutdown" " by server, not reusing", conn->connection_id, host, port); -#endif connclose(conn, "server shutdown"); - Curl_conn_terminate(data, conn, mdctx->premature); + Curl_conn_terminate(data, conn, (bool)mdctx->premature); } else { /* the connection is no longer in use by any transfer */ if(Curl_cpool_conn_now_idle(data, conn)) { /* connection kept in the cpool */ data->state.lastconnect_id = conn->connection_id; -#ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "Connection #%" FMT_OFF_T " to host %s:%d left intact", conn->connection_id, host, port); -#endif } else { /* connection was removed from the cpool and destroyed. */ @@ -688,10 +693,10 @@ static CURLcode multi_done(struct Curl_easy *data, case CURLE_ABORTED_BY_CALLBACK: case CURLE_READ_ERROR: case CURLE_WRITE_ERROR: - /* When we are aborted due to a callback return code it basically have to - be counted as premature as there is trouble ahead if we do not. We have - many callbacks and protocols work differently, we could potentially do - this more fine-grained in the future. */ + /* When we are aborted due to a callback return code it has to be counted + as premature as there is trouble ahead if we do not. We have many + callbacks and protocols work differently, we could potentially do this + more fine-grained in the future. */ premature = TRUE; FALLTHROUGH(); default: @@ -699,12 +704,12 @@ static CURLcode multi_done(struct Curl_easy *data, } /* this calls the protocol-specific function pointer previously set */ - if(conn->handler->done && (data->mstate >= MSTATE_PROTOCONNECT)) - result = conn->handler->done(data, status, premature); + if(conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT)) + result = conn->scheme->run->done(data, status, premature); else result = status; - if(CURLE_ABORTED_BY_CALLBACK != result) { + if(result != CURLE_ABORTED_BY_CALLBACK) { /* avoid this if we already aborted by callback to avoid this calling another callback */ int rc = Curl_pgrsDone(data); @@ -750,7 +755,6 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) bool premature; struct Curl_llist_node *e; CURLMcode mresult; - bool removed_timer = FALSE; uint32_t mid; /* First, make some basic checks that the CURLM handle is a good handle */ @@ -797,7 +801,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) /* multi_done() clears the association between the easy handle and the connection. - Note that this ignores the return code simply because there is + Note that this ignores the return code because there is nothing really useful to do with it anyway! */ (void)multi_done(data, data->result, premature); } @@ -805,7 +809,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) /* The timer must be shut down before data->multi is set to NULL, else the timenode will remain in the splay tree after curl_easy_cleanup is called. Do it after multi_done() in case that sets another time! */ - removed_timer = Curl_expire_clear(data); + Curl_expire_clear(data); /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) @@ -878,11 +882,9 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) We do not touch the easy handle here! */ process_pending_handles(multi); - if(removed_timer) { - mresult = Curl_update_timer(multi); - if(mresult) - return mresult; - } + mresult = Curl_update_timer(multi); + if(mresult) + return mresult; CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u", mid, Curl_multi_xfers_running(multi), @@ -936,8 +938,8 @@ void Curl_attach_connection(struct Curl_easy *data, conn->attached_multi = data->multi; DEBUGASSERT(conn->attached_multi == data->multi); - if(conn->handler && conn->handler->attach) - conn->handler->attach(data, conn); + if(conn->scheme && conn->scheme->run->attach) + conn->scheme->run->attach(data, conn); } /* adjust pollset for rate limits/pauses */ @@ -1015,8 +1017,8 @@ static CURLcode mstate_protocol_pollset(struct Curl_easy *data, struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(conn->handler->proto_pollset) - result = conn->handler->proto_pollset(data, ps); + if(conn->scheme->run->proto_pollset) + result = conn->scheme->run->proto_pollset(data, ps); else { curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; if(sockfd != CURL_SOCKET_BAD) { @@ -1037,8 +1039,8 @@ static CURLcode mstate_do_pollset(struct Curl_easy *data, struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(conn->handler->doing_pollset) - result = conn->handler->doing_pollset(data, ps); + if(conn->scheme->run->doing_pollset) + result = conn->scheme->run->doing_pollset(data, ps); else if(CONN_SOCK_IDX_VALID(conn->send_idx)) { /* Default is that we want to send something to the server */ result = Curl_pollset_add_out(data, ps, conn->sock[conn->send_idx]); @@ -1056,8 +1058,8 @@ static CURLcode mstate_domore_pollset(struct Curl_easy *data, struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(conn->handler->domore_pollset) - result = conn->handler->domore_pollset(data, ps); + if(conn->scheme->run->domore_pollset) + result = conn->scheme->run->domore_pollset(data, ps); else if(CONN_SOCK_IDX_VALID(conn->send_idx)) { /* Default is that we want to send something to the server */ result = Curl_pollset_add_out(data, ps, conn->sock[conn->send_idx]); @@ -1075,8 +1077,8 @@ static CURLcode mstate_perform_pollset(struct Curl_easy *data, struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(conn->handler->perform_pollset) - result = conn->handler->perform_pollset(data, ps); + if(conn->scheme->run->perform_pollset) + result = conn->scheme->run->perform_pollset(data, ps); else { /* Default is to obey the data->req.keepon flags for send/recv */ if(Curl_req_want_recv(data) && CONN_SOCK_IDX_VALID(conn->recv_idx)) { @@ -1170,7 +1172,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, goto out; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(CURL_TRC_M_is_verbose(data)) { size_t timeout_count = Curl_llist_count(&data->state.timeoutlist); switch(ps->n) { @@ -1245,17 +1247,10 @@ CURLMcode curl_multi_fdset(CURLM *m, if(!FDSET_SOCK(ps.sockets[i])) /* pretend it does not exist */ continue; -#ifdef __DJGPP__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warith-conversion" -#endif if(ps.actions[i] & CURL_POLL_IN) FD_SET(ps.sockets[i], read_fd_set); if(ps.actions[i] & CURL_POLL_OUT) FD_SET(ps.sockets[i], write_fd_set); -#ifdef __DJGPP__ -#pragma GCC diagnostic pop -#endif if((int)ps.sockets[i] > this_max_fd) this_max_fd = (int)ps.sockets[i]; } @@ -1331,7 +1326,7 @@ static void reset_socket_fdwrite(curl_socket_t s) int t; int l = (int)sizeof(t); if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM) - send(s, NULL, 0, 0); + swrite(s, NULL, 0); } #endif @@ -1466,7 +1461,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, #endif int pollrc; #ifdef USE_WINSOCK - if(cpfds.n) /* just pre-check with Winsock */ + if(cpfds.n) /* pre-check with Winsock */ pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0); else pollrc = 0; @@ -1540,20 +1535,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, #ifdef ENABLE_WAKEUP if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) { - char buf[64]; - ssize_t nread; - while(1) { - /* the reading socket is non-blocking, try to read - data from it until it receives an error (except EINTR). - In normal cases it will get EAGAIN or EWOULDBLOCK - when there is no more data, breaking the loop. */ - nread = wakeup_read(multi->wakeup_pair[0], buf, sizeof(buf)); - if(nread <= 0) { - if(nread < 0 && SOCKEINTR == SOCKERRNO) - continue; - break; - } - } + (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); /* do not count the wakeup socket into the returned value */ retcode--; } @@ -1630,38 +1612,9 @@ CURLMcode curl_multi_wakeup(CURLM *m) making it safe to access from another thread after the init part and before cleanup */ if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) { - while(1) { -#ifdef USE_EVENTFD - /* eventfd has a stringent rule of requiring the 8-byte buffer when - calling write(2) on it */ - const uint64_t buf[1] = { 1 }; -#else - const char buf[1] = { 1 }; -#endif - /* swrite() is not thread-safe in general, because concurrent calls - can have their messages interleaved, but in this case the content - of the messages does not matter, which makes it ok to call. - - The write socket is set to non-blocking, this way this function - cannot block, making it safe to call even from the same thread - that will call curl_multi_wait(). If swrite() returns that it - would block, it is considered successful because it means that - previous calls to this function will wake up the poll(). */ - if(wakeup_write(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) { - int err = SOCKERRNO; - int return_success; -#ifdef USE_WINSOCK - return_success = SOCKEWOULDBLOCK == err; -#else - if(SOCKEINTR == err) - continue; - return_success = SOCKEWOULDBLOCK == err || EAGAIN == err; -#endif - if(!return_success) - return CURLM_WAKEUP_FAILURE; - } - return CURLM_OK; - } + if(Curl_wakeup_signal(multi->wakeup_pair)) + return CURLM_WAKEUP_FAILURE; + return CURLM_OK; } #endif #endif @@ -1678,7 +1631,7 @@ CURLMcode curl_multi_wakeup(CURLM *m) */ static bool multi_ischanged(struct Curl_multi *multi, bool clear) { - bool retval = multi->recheckstate; + bool retval = (bool)multi->recheckstate; if(clear) multi->recheckstate = FALSE; return retval; @@ -1732,18 +1685,18 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; DEBUGASSERT(conn); - DEBUGASSERT(conn->handler); + DEBUGASSERT(conn->scheme); - if(conn->handler->do_it) - result = conn->handler->do_it(data, done); + if(conn->scheme->run->do_it) + result = conn->scheme->run->do_it(data, done); return result; } /* - * multi_do_more() is called during the DO_MORE multi state. It is basically a - * second stage DO state which (wrongly) was introduced to support FTP's - * second connection. + * multi_do_more() is called during the DO_MORE multi state. It is a second + * stage DO state which (wrongly) was introduced to support FTP's second + * connection. * * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to * DOING state there is more work to do! @@ -1756,8 +1709,8 @@ static CURLcode multi_do_more(struct Curl_easy *data, int *complete) *complete = 0; - if(conn->handler->do_more) - result = conn->handler->do_more(data, complete); + if(conn->scheme->run->do_more) + result = conn->scheme->run->do_more(data, complete); return result; } @@ -1769,14 +1722,13 @@ static bool multi_handle_timeout(struct Curl_easy *data, bool *stream_error, CURLcode *result) { - bool connect_timeout = data->mstate < MSTATE_DO; timediff_t timeout_ms; - timeout_ms = Curl_timeleft_ms(data, connect_timeout); + timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { /* Handle timed out */ struct curltime since; - if(connect_timeout) + if(Curl_is_connecting(data)) since = data->progress.t_startsingle; else since = data->progress.t_startop; @@ -1830,9 +1782,9 @@ static CURLcode protocol_connecting(struct Curl_easy *data, bool *done) CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - if(conn && conn->handler->connecting) { + if(conn && conn->scheme->run->connecting) { *done = FALSE; - result = conn->handler->connecting(data, done); + result = conn->scheme->run->connecting(data, done); } else *done = TRUE; @@ -1850,9 +1802,9 @@ static CURLcode protocol_doing(struct Curl_easy *data, bool *done) CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - if(conn && conn->handler->doing) { + if(conn && conn->scheme->run->doing) { *done = FALSE; - result = conn->handler->doing(data, done); + result = conn->scheme->run->doing(data, done); } else *done = TRUE; @@ -1876,9 +1828,9 @@ static CURLcode protocol_connect(struct Curl_easy *data, bool *protocol_done) *protocol_done = FALSE; if(!conn->bits.protoconnstart) { - if(conn->handler->connect_it) { + if(conn->scheme->run->connect_it) { /* Call the protocol-specific connect function */ - result = conn->handler->connect_it(data, protocol_done); + result = conn->scheme->run->connect_it(data, protocol_done); if(result) return result; } @@ -1887,7 +1839,7 @@ static CURLcode protocol_connect(struct Curl_easy *data, bool *protocol_done) /* Unless this protocol does not have any protocol-connect callback, as then we know we are done. */ - if(!conn->handler->connecting) + if(!conn->scheme->run->connecting) *protocol_done = TRUE; return CURLE_OK; } @@ -1902,7 +1854,7 @@ static void set_in_callback(struct Curl_multi *multi, bool value) */ static void multi_posttransfer(struct Curl_easy *data) { -#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(MSG_NOSIGNAL) /* restore the signal handler for SIGPIPE before we get back */ if(!data->set.no_signal) signal(SIGPIPE, data->state.prev_signal); @@ -1918,36 +1870,53 @@ static void multi_posttransfer(struct Curl_easy *data) * This function DOES NOT FREE the given url. */ static CURLcode multi_follow(struct Curl_easy *data, - const struct Curl_handler *handler, + const struct Curl_scheme *handler, const char *newurl, /* the Location: string */ followtype type) /* see transfer.h */ { - if(handler && handler->follow) - return handler->follow(data, newurl, type); + if(handler && handler->run->follow) + return handler->run->follow(data, newurl, type); return CURLE_TOO_MANY_REDIRECTS; } static CURLcode mspeed_check(struct Curl_easy *data) { - const struct curltime *pnow = Curl_pgrs_now(data); - timediff_t recv_wait_ms = 0; - timediff_t send_wait_ms = 0; + if(Curl_rlimit_active(&data->progress.dl.rlimit) || + Curl_rlimit_active(&data->progress.ul.rlimit)) { + /* check if our send/recv limits require idle waits */ + const struct curltime *pnow = Curl_pgrs_now(data); + timediff_t recv_ms, send_ms; - /* check if our send/recv limits require idle waits */ - send_wait_ms = Curl_rlimit_wait_ms(&data->progress.ul.rlimit, pnow); - recv_wait_ms = Curl_rlimit_wait_ms(&data->progress.dl.rlimit, pnow); + send_ms = Curl_rlimit_wait_ms(&data->progress.ul.rlimit, pnow); + recv_ms = Curl_rlimit_wait_ms(&data->progress.dl.rlimit, pnow); - if(send_wait_ms || recv_wait_ms) { - if(data->mstate != MSTATE_RATELIMITING) { - multistate(data, MSTATE_RATELIMITING); + if(send_ms || recv_ms) { + if(data->mstate != MSTATE_RATELIMITING) { + multistate(data, MSTATE_RATELIMITING); + } + Curl_expire(data, CURLMAX(send_ms, recv_ms), EXPIRE_TOOFAST); + Curl_multi_clear_dirty(data); + CURL_TRC_M(data, "[RLIMIT] waiting %" FMT_TIMEDIFF_T "ms", + CURLMAX(send_ms, recv_ms)); + return CURLE_AGAIN; + } + else { + /* when will the rate limits increase next? The transfer needs + * to run again at that time or it may stall. */ + send_ms = Curl_rlimit_next_step_ms(&data->progress.ul.rlimit, pnow); + recv_ms = Curl_rlimit_next_step_ms(&data->progress.dl.rlimit, pnow); + if(send_ms || recv_ms) { + timediff_t next_ms = CURLMIN(send_ms, recv_ms); + if(!next_ms) + next_ms = CURLMAX(send_ms, recv_ms); + Curl_expire(data, next_ms, EXPIRE_TOOFAST); + CURL_TRC_M(data, "[RLIMIT] next token update in %" FMT_TIMEDIFF_T "ms", + next_ms); + } } - Curl_expire(data, CURLMAX(send_wait_ms, recv_wait_ms), EXPIRE_TOOFAST); - Curl_multi_clear_dirty(data); - CURL_TRC_M(data, "[RLIMIT] waiting %" FMT_TIMEDIFF_T "ms", - CURLMAX(send_wait_ms, recv_wait_ms)); - return CURLE_AGAIN; } - else if(data->mstate != MSTATE_PERFORMING) { + + if(data->mstate != MSTATE_PERFORMING) { CURL_TRC_M(data, "[RLIMIT] wait over, continue"); multistate(data, MSTATE_PERFORMING); } @@ -1989,7 +1958,7 @@ static CURLMcode state_performing(struct Curl_easy *data, } } #ifndef CURL_DISABLE_HTTP - else if((CURLE_HTTP2_STREAM == result) && + else if((result == CURLE_HTTP2_STREAM) && Curl_h2_http_1_1_error(data)) { CURLcode ret = Curl_retry_request(data, &newurl); @@ -2028,7 +1997,7 @@ static CURLMcode state_performing(struct Curl_easy *data, * connection. */ - if(!(data->conn->handler->flags & PROTOPT_DUAL) && + if(!(data->conn->scheme->flags & PROTOPT_DUAL) && result != CURLE_HTTP2_STREAM) streamclose(data->conn, "Transfer returned error"); @@ -2036,7 +2005,7 @@ static CURLMcode state_performing(struct Curl_easy *data, multi_done(data, result, TRUE); } else if(data->req.done && !Curl_cwriter_is_paused(data)) { - const struct Curl_handler *handler = data->conn->handler; + const struct Curl_scheme *handler = data->conn->scheme; /* call this even if the readwrite function returned error */ multi_posttransfer(data); @@ -2046,7 +2015,7 @@ static CURLMcode state_performing(struct Curl_easy *data, if(data->req.newurl || retry) { followtype follow = FOLLOW_NONE; if(!retry) { - /* if the URL is a follow-location and not just a retried request then + /* if the URL is a follow-location and not a retried request then figure out the URL here */ curlx_free(newurl); newurl = data->req.newurl; @@ -2168,14 +2137,14 @@ static CURLMcode state_do(struct Curl_easy *data, mresult = CURLM_CALL_MULTI_PERFORM; } } - else if((CURLE_SEND_ERROR == result) && + else if((result == CURLE_SEND_ERROR) && data->conn->bits.reuse) { /* * In this situation, a connection that we were trying to use may have * unexpectedly died. If possible, send the connection back to the * CONNECT phase so we can try again. */ - const struct Curl_handler *handler = data->conn->handler; + const struct Curl_scheme *handler = data->conn->scheme; char *newurl = NULL; followtype follow = FOLLOW_NONE; CURLcode drc; @@ -2240,7 +2209,7 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data, result = Curl_pgrsCheck(data); if(result) { - if(!(data->conn->handler->flags & PROTOPT_DUAL) && + if(!(data->conn->scheme->flags & PROTOPT_DUAL) && result != CURLE_HTTP2_STREAM) streamclose(data->conn, "Transfer returned error"); @@ -2315,7 +2284,7 @@ static CURLMcode state_connect(struct Curl_multi *multi, bool async; CURLMcode mresult = CURLM_OK; CURLcode result = Curl_connect(data, &async, &connected); - if(CURLE_NO_CONNECTION_AVAILABLE == result) { + if(result == CURLE_NO_CONNECTION_AVAILABLE) { /* There was no connection available. We will go to the pending state and wait for an available connection. */ multistate(data, MSTATE_PENDING); @@ -2356,10 +2325,111 @@ static CURLMcode state_connect(struct Curl_multi *multi, return mresult; } +/* returns the possibly updated result */ +static CURLcode is_finished(struct Curl_multi *multi, + struct Curl_easy *data, + bool stream_error, + CURLcode result) +{ + if(data->mstate < MSTATE_COMPLETED) { + if(result) { + /* + * If an error was returned, and we are not in completed state now, + * then we go to completed and consider this transfer aborted. + */ + + /* No attempt to disconnect connections must be made before this - + connection detach and termination happens only here */ + + /* Check if we can move pending requests to send pipe */ + process_pending_handles(multi); /* connection */ + + if(data->conn) { + if(stream_error) { + /* Do not attempt to send data over a connection that timed out */ + bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; + struct connectdata *conn = data->conn; + + /* This is where we make sure that the conn pointer is reset. + We do not have to do this in every case block above where a + failure is detected */ + Curl_detach_connection(data); + Curl_conn_terminate(data, conn, dead_connection); + } + } + else if(data->mstate == MSTATE_CONNECT) { + /* Curl_connect() failed */ + multi_posttransfer(data); + Curl_pgrsUpdate_nometer(data); + } + + multistate(data, MSTATE_COMPLETED); + return result; + } + /* if there is still a connection to use, call the progress function */ + else if(data->conn) { + result = Curl_pgrsUpdate(data); + if(result) { + /* aborted due to progress callback return code must close the + connection */ + streamclose(data->conn, "Aborted by callback"); + + /* if not yet in DONE state, go there, otherwise COMPLETED */ + multistate(data, (data->mstate < MSTATE_DONE) ? + MSTATE_DONE : MSTATE_COMPLETED); + return result; + } + } + } + return result; +} + +static void handle_completed(struct Curl_multi *multi, + struct Curl_easy *data, + CURLcode result) +{ + if(data->master_mid != UINT32_MAX) { + /* A sub transfer, not for msgsent to application */ + struct Curl_easy *mdata; + + CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid); + mdata = Curl_multi_get_easy(multi, data->master_mid); + if(mdata) { + if(mdata->sub_xfer_done) + mdata->sub_xfer_done(mdata, data, result); + else + CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.", + data->master_mid); + } + else { + CURL_TRC_M(data, "master easy %u already gone.", data->master_mid); + } + } + else { + /* now fill in the Curl_message with this info */ + struct Curl_message *msg = &data->msg; + + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = data; + msg->extmsg.data.result = result; + + multi_addmsg(multi, msg); + DEBUGASSERT(!data->conn); + } + multistate(data, MSTATE_MSGSENT); + + /* remove from the other sets, add to msgsent */ + Curl_uint32_bset_remove(&multi->process, data->mid); + Curl_uint32_bset_remove(&multi->dirty, data->mid); + Curl_uint32_bset_remove(&multi->pending, data->mid); + Curl_uint32_bset_add(&multi->msgsent, data->mid); + --multi->xfers_alive; +} + static CURLMcode multi_runsingle(struct Curl_multi *multi, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_sigpipe_ctx *sigpipe_ctx) { - struct Curl_message *msg = NULL; bool connected; bool protocol_connected = FALSE; bool dophase_done = FALSE; @@ -2386,10 +2456,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); if(data == multi->admin) { - Curl_cshutdn_perform(&multi->cshutdn, multi->admin, CURL_SOCKET_TIMEOUT); + Curl_cshutdn_perform(&multi->cshutdn, multi->admin, sigpipe_ctx); return CURLM_OK; } + sigpipe_apply(data, sigpipe_ctx); do { /* A "stream" here is a logical stream if the protocol can handle that (HTTP/2), or the full connection for older protocols */ @@ -2426,7 +2497,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* after init, go SETUP */ multistate(data, MSTATE_SETUP); - (void)Curl_pgrsTime(data, TIMER_STARTOP); + Curl_pgrsTime(data, TIMER_STARTOP); FALLTHROUGH(); case MSTATE_SETUP: @@ -2586,7 +2657,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { #ifndef CURL_DISABLE_FTP if(data->state.wildcardmatch && - ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) { + ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) { data->wildcard->state = CURLWC_DONE; } #endif @@ -2660,94 +2731,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, statemachine_end: - if(data->mstate < MSTATE_COMPLETED) { - if(result) { - /* - * If an error was returned, and we are not in completed state now, - * then we go to completed and consider this transfer aborted. - */ - - /* NOTE: no attempt to disconnect connections must be made - in the case blocks above - cleanup happens only here */ - - /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* connection */ - - if(data->conn) { - if(stream_error) { - /* Do not attempt to send data over a connection that timed out */ - bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; - struct connectdata *conn = data->conn; - - /* This is where we make sure that the conn pointer is reset. - We do not have to do this in every case block above where a - failure is detected */ - Curl_detach_connection(data); - Curl_conn_terminate(data, conn, dead_connection); - } - } - else if(data->mstate == MSTATE_CONNECT) { - /* Curl_connect() failed */ - multi_posttransfer(data); - Curl_pgrsUpdate_nometer(data); - } - - multistate(data, MSTATE_COMPLETED); - mresult = CURLM_CALL_MULTI_PERFORM; - } - /* if there is still a connection to use, call the progress function */ - else if(data->conn) { - result = Curl_pgrsUpdate(data); - if(result) { - /* aborted due to progress callback return code must close the - connection */ - streamclose(data->conn, "Aborted by callback"); - - /* if not yet in DONE state, go there, otherwise COMPLETED */ - multistate(data, (data->mstate < MSTATE_DONE) ? - MSTATE_DONE : MSTATE_COMPLETED); - mresult = CURLM_CALL_MULTI_PERFORM; - } - } - } + result = is_finished(multi, data, stream_error, result); + if(result) + mresult = CURLM_CALL_MULTI_PERFORM; if(MSTATE_COMPLETED == data->mstate) { - if(data->master_mid != UINT32_MAX) { - /* A sub transfer, not for msgsent to application */ - struct Curl_easy *mdata; - - CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid); - mdata = Curl_multi_get_easy(multi, data->master_mid); - if(mdata) { - if(mdata->sub_xfer_done) - mdata->sub_xfer_done(mdata, data, result); - else - CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.", - data->master_mid); - } - else { - CURL_TRC_M(data, "master easy %u already gone.", data->master_mid); - } - } - else { - /* now fill in the Curl_message with this info */ - msg = &data->msg; - - msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = data; - msg->extmsg.data.result = result; - - multi_addmsg(multi, msg); - DEBUGASSERT(!data->conn); - } - multistate(data, MSTATE_MSGSENT); - - /* remove from the other sets, add to msgsent */ - Curl_uint32_bset_remove(&multi->process, data->mid); - Curl_uint32_bset_remove(&multi->dirty, data->mid); - Curl_uint32_bset_remove(&multi->pending, data->mid); - Curl_uint32_bset_add(&multi->msgsent, data->mid); - --multi->xfers_alive; + handle_completed(multi, data, result); return CURLM_OK; } } while((mresult == CURLM_CALL_MULTI_PERFORM) || @@ -2763,7 +2752,7 @@ static CURLMcode multi_perform(struct Curl_multi *multi, CURLMcode returncode = CURLM_OK; struct curltime start = *multi_now(multi); uint32_t mid; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; @@ -2771,7 +2760,8 @@ static CURLMcode multi_perform(struct Curl_multi *multi, if(multi->in_ntfy_callback) return CURLM_RECURSIVE_API_CALL; - sigpipe_init(&pipe_st); + sigpipe_init(&sigpipe_ctx); + if(Curl_uint32_bset_first(&multi->process, &mid)) { CURL_TRC_M(multi->admin, "multi_perform(running=%u)", Curl_multi_xfers_running(multi)); @@ -2784,13 +2774,12 @@ static CURLMcode multi_perform(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, mid); continue; } - sigpipe_apply(data, &pipe_st); - mresult = multi_runsingle(multi, data); + mresult = multi_runsingle(multi, data, &sigpipe_ctx); if(mresult) returncode = mresult; } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } - sigpipe_restore(&pipe_st); + sigpipe_restore(&sigpipe_ctx); if(multi_ischanged(multi, TRUE)) process_pending_handles(multi); @@ -2799,7 +2788,7 @@ static CURLMcode multi_perform(struct Curl_multi *multi, returncode = Curl_mntfy_dispatch_all(multi); /* - * Simply remove all expired timers from the splay since handles are dealt + * Remove all expired timers from the splay since handles are dealt * with unconditionally by this function and curl_multi_timeout() requires * that already passed/handled expire times are removed from the splay. * @@ -2919,10 +2908,7 @@ CURLMcode curl_multi_cleanup(CURLM *m) WSACloseEvent(multi->wsa_event); #else #ifdef ENABLE_WAKEUP - wakeup_close(multi->wakeup_pair[0]); -#ifndef USE_EVENTFD - wakeup_close(multi->wakeup_pair[1]); -#endif + Curl_wakeup_destroy(multi->wakeup_pair); #endif #endif @@ -2999,7 +2985,7 @@ void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) * add_next_timeout() * * Each Curl_easy has a list of timeouts. The add_next_timeout() is called - * when it has just been removed from the splay tree because the timeout has + * when it has been removed from the splay tree because the timeout has * expired. This function is then to advance in the list to pick the next * timeout to use (skip the already expired ones) and add this node back to * the splay tree again. @@ -3050,22 +3036,15 @@ static CURLMcode add_next_timeout(const struct curltime *pnow, return CURLM_OK; } -struct multi_run_ctx { - struct Curl_multi *multi; - size_t run_xfers; - SIGPIPE_MEMBER(pipe_st); -}; - -static void multi_mark_expired_as_dirty(struct multi_run_ctx *mrc, +static void multi_mark_expired_as_dirty(struct Curl_multi *multi, const struct curltime *ts) { - struct Curl_multi *multi = mrc->multi; struct Curl_easy *data = NULL; struct Curl_tree *t = NULL; /* * The loop following here will go on as long as there are expire-times left - * to process (compared to mrc->now) in the splay and 'data' will be + * to process (compared to `ts`) in the splay and 'data' will be * re-assigned for every expired handle we deal with. */ while(1) { @@ -3078,7 +3057,7 @@ static void multi_mark_expired_as_dirty(struct multi_run_ctx *mrc, data = Curl_splayget(t); /* assign this for next loop */ if(!data) continue; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(CURL_TRC_TIMER_is_verbose(data)) { struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); if(e) { @@ -3092,12 +3071,14 @@ static void multi_mark_expired_as_dirty(struct multi_run_ctx *mrc, } } -static CURLMcode multi_run_dirty(struct multi_run_ctx *mrc) +static CURLMcode multi_run_dirty(struct Curl_multi *multi, + struct Curl_sigpipe_ctx *sigpipe_ctx, + uint32_t *pnum) { - struct Curl_multi *multi = mrc->multi; CURLMcode mresult = CURLM_OK; uint32_t mid; + *pnum = 0; if(Curl_uint32_bset_first(&multi->dirty, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); @@ -3110,10 +3091,9 @@ static CURLMcode multi_run_dirty(struct multi_run_ctx *mrc) continue; } - mrc->run_xfers++; - sigpipe_apply(data, &mrc->pipe_st); + (*pnum)++; /* runsingle() clears the dirty mid */ - mresult = multi_runsingle(multi, data); + mresult = multi_runsingle(multi, data, sigpipe_ctx); if(CURLM_OK >= mresult) { /* reassess event handling of data */ @@ -3140,12 +3120,11 @@ static CURLMcode multi_socket(struct Curl_multi *multi, int *running_handles) { CURLMcode mresult = CURLM_OK; - struct multi_run_ctx mrc; + struct Curl_sigpipe_ctx pipe_ctx; + uint32_t run_xfers; (void)ev_bitmask; - memset(&mrc, 0, sizeof(mrc)); - mrc.multi = multi; - sigpipe_init(&mrc.pipe_st); + sigpipe_init(&pipe_ctx); if(checkall) { /* *perform() deals with running_handles on its own */ @@ -3171,23 +3150,23 @@ static CURLMcode multi_socket(struct Curl_multi *multi, memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts)); } - multi_mark_expired_as_dirty(&mrc, multi_now(multi)); - mresult = multi_run_dirty(&mrc); + multi_mark_expired_as_dirty(multi, multi_now(multi)); + mresult = multi_run_dirty(multi, &pipe_ctx, &run_xfers); if(mresult) goto out; - if(mrc.run_xfers) { + if(run_xfers) { /* Running transfers takes time. With a new timestamp, we might catch * other expires which are due now. Instead of telling the application * to set a 0 timeout and call us again, we run them here. * Do that only once or it might be unfair to transfers on other * sockets. */ - multi_mark_expired_as_dirty(&mrc, &multi->now); - mresult = multi_run_dirty(&mrc); + multi_mark_expired_as_dirty(multi, &multi->now); + mresult = multi_run_dirty(multi, &pipe_ctx, &run_xfers); } out: - sigpipe_restore(&mrc.pipe_st); + sigpipe_restore(&pipe_ctx); if(multi_ischanged(multi, TRUE)) process_pending_handles(multi); @@ -3358,9 +3337,7 @@ static void multi_timeout(struct Curl_multi *multi, long *timeout_ms) { static const struct curltime tv_zero = { 0, 0 }; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - struct Curl_easy *data = NULL; -#endif + VERBOSE(struct Curl_easy *data = NULL); if(multi->dead) { *timeout_ms = 0; @@ -3387,19 +3364,14 @@ static void multi_timeout(struct Curl_multi *multi, /* some time left before expiration */ timediff_t diff_ms = curlx_timediff_ceil_ms(multi->timetree->key, *pnow); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - data = Curl_splayget(multi->timetree); -#endif + VERBOSE(data = Curl_splayget(multi->timetree)); /* this should be safe even on 32-bit archs, as we do not use that overly long timeouts */ *timeout_ms = (long)diff_ms; } else { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(multi->timetree) { - data = Curl_splayget(multi->timetree); - } -#endif + if(multi->timetree) + VERBOSE(data = Curl_splayget(multi->timetree)); /* 0 means immediately */ *timeout_ms = 0; } @@ -3409,8 +3381,8 @@ static void multi_timeout(struct Curl_multi *multi, *timeout_ms = -1; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(data && CURL_TRC_TIMER_is_verbose(data)) { +#ifdef CURLVERBOSE + if(CURL_TRC_TIMER_is_verbose(data)) { struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); if(e) { struct time_node *n = Curl_node_elem(e); @@ -3579,7 +3551,7 @@ void Curl_expire_ex(struct Curl_easy *data, set.tv_usec -= 1000000; } - /* Remove any timer with the same id just in case. */ + /* Remove any timer with the same id */ multi_deltimeout(data, id); /* Add it to the timer list. It must stay in the list until it has expired @@ -3637,11 +3609,11 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) * Removes the expire timer. Marks it as done. * */ -void Curl_expire_done(struct Curl_easy *data, expire_id eid) +void Curl_expire_done(struct Curl_easy *data, expire_id id) { /* remove the timer, if there */ - multi_deltimeout(data, eid); - CURL_TRC_TIMER(data, eid, "cleared"); + multi_deltimeout(data, id); + CURL_TRC_TIMER(data, id, "cleared"); } /* @@ -3649,7 +3621,7 @@ void Curl_expire_done(struct Curl_easy *data, expire_id eid) * * Clear ALL timeout values for this handle. */ -bool Curl_expire_clear(struct Curl_easy *data) +void Curl_expire_clear(struct Curl_easy *data) { struct Curl_multi *multi = data->multi; struct curltime *nowp = &data->state.expiretime; @@ -3657,7 +3629,7 @@ bool Curl_expire_clear(struct Curl_easy *data) /* this is only interesting while there is still an associated multi struct remaining! */ if(!multi) - return FALSE; + return; if(nowp->tv_sec || nowp->tv_usec) { /* Since this is an cleared time, we must remove the previous entry from @@ -3677,9 +3649,7 @@ bool Curl_expire_clear(struct Curl_easy *data) CURL_TRC_M(data, "[TIMEOUT] all cleared"); nowp->tv_sec = 0; nowp->tv_usec = 0; - return TRUE; } - return FALSE; } CURLMcode curl_multi_assign(CURLM *m, curl_socket_t s, @@ -3757,7 +3727,7 @@ CURL **curl_multi_get_handles(CURLM *m) { struct Curl_multi *multi = m; void *entry; - unsigned int count = Curl_uint32_tbl_count(&multi->xfers); + size_t count = Curl_uint32_tbl_count(&multi->xfers); CURL **a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1)); if(a) { unsigned int i = 0; @@ -3986,7 +3956,7 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, uint32_t mid) { struct Curl_easy *data = Curl_uint32_tbl_get(&multi->xfers, mid); - if(data && GOOD_EASY_HANDLE(data)) + if(GOOD_EASY_HANDLE(data)) return data; CURL_TRC_M(multi->admin, "invalid easy handle in xfer table for mid=%u", mid); diff --git a/vendor/curl/lib/multi_ev.c b/vendor/curl/lib/multi_ev.c index 0c89721..696a012 100644 --- a/vendor/curl/lib/multi_ev.c +++ b/vendor/curl/lib/multi_ev.c @@ -195,6 +195,7 @@ static CURLMcode mev_forget_socket(struct Curl_multi *multi, /* We managed this socket before, tell the socket callback to forget it. */ if(entry->announced && multi->socket_cb) { + NOVERBOSE((void)cause); CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s); mev_in_callback(multi, TRUE); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, @@ -567,9 +568,9 @@ void Curl_multi_ev_dirty_xfers(struct Curl_multi *multi, /* Unmatched socket, we cannot act on it but we ignore this fact. In real-world tests it has been proved that libevent can in fact give - the application actions even though the socket was just previously + the application actions even though the socket was previously asked to get removed, so thus we better survive stray socket actions - and just move on. */ + and move on. */ if(entry) { struct Curl_easy *data; uint32_t mid; diff --git a/vendor/curl/lib/multi_ntfy.c b/vendor/curl/lib/multi_ntfy.c index 4921fc5..1319aae 100644 --- a/vendor/curl/lib/multi_ntfy.c +++ b/vendor/curl/lib/multi_ntfy.c @@ -110,7 +110,7 @@ static void mntfy_chunk_dispatch_all(struct Curl_multi *multi, /* only when notification has not been disabled in the meantime */ if(data && Curl_uint32_bset_contains(&multi->ntfy.enabled, e->type)) { /* this may cause new notifications to be added! */ - CURL_TRC_M(multi->admin, "[NTFY] dispatch %d to xfer %u", + CURL_TRC_M(multi->admin, "[NTFY] dispatch %u to xfer %u", e->type, e->mid); multi->ntfy.ntfy_cb(multi, e->type, data, multi->ntfy.ntfy_cb_data); } @@ -168,7 +168,7 @@ void Curl_mntfy_add(struct Curl_easy *data, unsigned int type) Curl_uint32_bset_contains(&multi->ntfy.enabled, (uint32_t)type)) { /* append to list of outstanding notifications */ struct mntfy_chunk *tail = mntfy_non_full_tail(&multi->ntfy); - CURL_TRC_M(data, "[NTFY] add %d for xfer %u", type, data->mid); + CURL_TRC_M(data, "[NTFY] add %u for xfer %u", type, data->mid); if(tail) mntfy_chunk_append(tail, data, (uint32_t)type); else diff --git a/vendor/curl/lib/multihandle.h b/vendor/curl/lib/multihandle.h index f66853f..7a56112 100644 --- a/vendor/curl/lib/multihandle.h +++ b/vendor/curl/lib/multihandle.h @@ -174,10 +174,7 @@ struct Curl_multi { #ifdef DEBUGBUILD unsigned int now_access_count; #endif -#define IPV6_UNKNOWN 0 -#define IPV6_DEAD 1 -#define IPV6_WORKS 2 - unsigned char ipv6_up; /* IPV6_* defined */ + BIT(ipv6_works); BIT(multiplexing); /* multiplexing wanted */ BIT(recheckstate); /* see Curl_multi_connchanged */ BIT(in_callback); /* true while executing a callback */ diff --git a/vendor/curl/lib/multiif.h b/vendor/curl/lib/multiif.h index 15fc070..039db26 100644 --- a/vendor/curl/lib/multiif.h +++ b/vendor/curl/lib/multiif.h @@ -27,10 +27,10 @@ * Prototypes for library-wide functions provided by multi.c */ -void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id); +void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id); void Curl_expire_ex(struct Curl_easy *data, timediff_t milli, expire_id id); -bool Curl_expire_clear(struct Curl_easy *data); +void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connection(struct Curl_easy *data, @@ -40,13 +40,14 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi); void Curl_set_in_callback(struct Curl_easy *data, bool value); bool Curl_is_in_callback(struct Curl_easy *data); CURLcode Curl_preconnect(struct Curl_easy *data); +bool Curl_is_connecting(struct Curl_easy *data); void Curl_multi_connchanged(struct Curl_multi *multi); /* Internal version of curl_multi_init() accepts size parameters for the socket, connection and dns hashes */ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, - size_t hashsize, + size_t ev_hashsize, size_t chashsize, size_t dnssize, size_t sesssize); diff --git a/vendor/curl/lib/netrc.c b/vendor/curl/lib/netrc.c index 0678733..1073cc5 100644 --- a/vendor/curl/lib/netrc.c +++ b/vendor/curl/lib/netrc.c @@ -122,7 +122,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, any order */ bool our_login = FALSE; /* found our login name */ bool done = FALSE; - char *netrcbuffer; + const char *netrcbuffer; struct dynbuf token; struct dynbuf *filebuf = &store->filebuf; DEBUGASSERT(!*passwordp); @@ -151,7 +151,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, } if(!*tok || (*tok == '\n')) - /* end of line */ + /* end of line */ break; /* leading double-quote means quoted string */ @@ -326,7 +326,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, tok = ++tok_end; } if(!done) { - char *nl = NULL; + const char *nl = NULL; if(tok) nl = strchr(tok, '\n'); if(!nl) @@ -464,14 +464,14 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, return retcode; } -void Curl_netrc_init(struct store_netrc *s) +void Curl_netrc_init(struct store_netrc *store) { - curlx_dyn_init(&s->filebuf, MAX_NETRC_FILE); - s->loaded = FALSE; + curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE); + store->loaded = FALSE; } -void Curl_netrc_cleanup(struct store_netrc *s) +void Curl_netrc_cleanup(struct store_netrc *store) { - curlx_dyn_free(&s->filebuf); - s->loaded = FALSE; + curlx_dyn_free(&store->filebuf); + store->loaded = FALSE; } #endif diff --git a/vendor/curl/lib/netrc.h b/vendor/curl/lib/netrc.h index 99ddc62..061e1e4 100644 --- a/vendor/curl/lib/netrc.h +++ b/vendor/curl/lib/netrc.h @@ -45,12 +45,12 @@ typedef enum { } NETRCcode; const char *Curl_netrc_strerror(NETRCcode ret); -void Curl_netrc_init(struct store_netrc *s); -void Curl_netrc_cleanup(struct store_netrc *s); +void Curl_netrc_init(struct store_netrc *store); +void Curl_netrc_cleanup(struct store_netrc *store); -NETRCcode Curl_parsenetrc(struct store_netrc *s, const char *host, +NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, char **loginp, char **passwordp, - const char *filename); + const char *netrcfile); /* Assume: (*passwordp)[0]=0, host[0] != 0. * If (*loginp)[0] = 0, search for login and password within a machine * section in the netrc. diff --git a/vendor/curl/lib/noproxy.c b/vendor/curl/lib/noproxy.c index 541765b..ee03fd3 100644 --- a/vendor/curl/lib/noproxy.c +++ b/vendor/curl/lib/noproxy.c @@ -148,7 +148,6 @@ static bool match_host(const char *token, size_t tokenlen, static bool match_ip(int type, const char *token, size_t tokenlen, const char *name) { - const char *check = token; char *slash; unsigned int bits = 0; char checkip[128]; @@ -156,11 +155,10 @@ static bool match_ip(int type, const char *token, size_t tokenlen, /* this cannot match */ return FALSE; /* copy the check name to a temp buffer */ - memcpy(checkip, check, tokenlen); + memcpy(checkip, token, tokenlen); checkip[tokenlen] = 0; - check = checkip; - slash = strchr(check, '/'); + slash = strchr(checkip, '/'); /* if the slash is part of this token, use it */ if(slash) { curl_off_t value; @@ -172,9 +170,9 @@ static bool match_ip(int type, const char *token, size_t tokenlen, *slash = 0; /* null-terminate there */ } if(type == TYPE_IPV6) - return Curl_cidr6_match(name, check, bits); + return Curl_cidr6_match(name, checkip, bits); else - return Curl_cidr4_match(name, check, bits); + return Curl_cidr4_match(name, checkip, bits); } /**************************************************************** @@ -203,7 +201,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy) if(!strcmp("*", no_proxy)) return TRUE; - /* NO_PROXY was specified and it was not just an asterisk */ + /* NO_PROXY was specified and it was not only an asterisk */ /* Check if name is an IP address; if not, assume it being a hostname. */ namelen = strlen(name); @@ -253,7 +251,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy) while(*p == ',') p++; } /* while(*p) */ - } /* NO_PROXY was specified and it was not just an asterisk */ + } /* NO_PROXY was specified and it was not only an asterisk */ return FALSE; } diff --git a/vendor/curl/lib/openldap.c b/vendor/curl/lib/openldap.c index f826a4d..95f7681 100644 --- a/vendor/curl/lib/openldap.c +++ b/vendor/curl/lib/openldap.c @@ -91,98 +91,8 @@ extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); #endif -static CURLcode oldap_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode oldap_do(struct Curl_easy *data, bool *done); -static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool); -static CURLcode oldap_connect(struct Curl_easy *data, bool *done); -static CURLcode oldap_connecting(struct Curl_easy *data, bool *done); -static CURLcode oldap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); - -static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out); - static Curl_recv oldap_recv; -/* - * LDAP protocol handler. - */ -const struct Curl_handler Curl_handler_ldap = { - "ldap", /* scheme */ - oldap_setup_connection, /* setup_connection */ - oldap_do, /* do_it */ - oldap_done, /* done */ - ZERO_NULL, /* do_more */ - oldap_connect, /* connect_it */ - oldap_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - oldap_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_LDAP, /* defport */ - CURLPROTO_LDAP, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL_REUSE | /* flags */ - PROTOPT_CONN_REUSE -}; - -#ifdef USE_SSL -/* - * LDAPS protocol handler. - */ -const struct Curl_handler Curl_handler_ldaps = { - "ldaps", /* scheme */ - oldap_setup_connection, /* setup_connection */ - oldap_do, /* do_it */ - oldap_done, /* done */ - ZERO_NULL, /* do_more */ - oldap_connect, /* connect_it */ - oldap_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - oldap_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_LDAPS, /* defport */ - CURLPROTO_LDAPS, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL | /* flags */ - PROTOPT_CONN_REUSE -}; -#endif - -/* SASL parameters for the ldap protocol */ -static const struct SASLproto saslldap = { - "ldap", /* The service name */ - oldap_perform_auth, /* Send authentication command */ - oldap_continue_auth, /* Send authentication continuation */ - oldap_cancel_auth, /* Send authentication cancellation */ - oldap_get_message, /* Get SASL response message */ - 0, /* Maximum initial response length (no max) */ - LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ - LDAP_SUCCESS, /* Code to receive upon authentication success */ - SASL_AUTH_NONE, /* Default mechanisms */ - 0 /* Configuration flags */ -}; - struct ldapconninfo { struct SASL sasl; /* SASL-related parameters */ LDAP *ld; /* Openldap connection handle. */ @@ -204,7 +114,6 @@ struct ldapreqinfo { /* meta key for storing ldapconninfo at connection */ #define CURL_META_LDAP_CONN "meta:proto:ldap:conn" - /* * oldap_state() * @@ -213,7 +122,7 @@ struct ldapreqinfo { static void oldap_state(struct Curl_easy *data, struct ldapconninfo *li, ldapstate newstate) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* for debug purposes */ static const char * const names[] = { "STOP", @@ -230,8 +139,9 @@ static void oldap_state(struct Curl_easy *data, struct ldapconninfo *li, if(li->state != newstate) infof(data, "LDAP %p state change from %s to %s", (void *)li, names[li->state], names[newstate]); -#endif +#else (void)data; +#endif li->state = newstate; } @@ -426,7 +336,7 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) { struct connectdata *conn = data->conn; struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - char *binddn = NULL; + const char *binddn = NULL; struct berval passwd; int rc; @@ -496,14 +406,86 @@ static CURLcode oldap_perform_sasl(struct Curl_easy *data) } #ifdef USE_SSL -static int ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg); -static int ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod); -static int ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg); +static int ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) +{ + sbiod->sbiod_pvt = arg; + return 0; +} + +static int ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) +{ + sbiod->sbiod_pvt = NULL; + return 0; +} + +/* We do not need to do anything because libcurl does it already */ +static int ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) +{ + (void)sbiod; + return 0; +} + +static int ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) +{ + (void)arg; + if(opt == LBER_SB_OPT_DATA_READY) { + struct Curl_easy *data = sbiod->sbiod_pvt; + return Curl_conn_data_pending(data, FIRSTSOCKET); + } + return 0; +} + static ber_slen_t ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len); + ber_len_t len) +{ + struct Curl_easy *data = sbiod->sbiod_pvt; + ber_slen_t ret = 0; + if(data) { + struct connectdata *conn = data->conn; + if(conn) { + struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + CURLcode err = CURLE_RECV_ERROR; + size_t nread; + + if(!li) { + SET_SOCKERRNO(SOCKEINVAL); + return -1; + } + err = (li->recv)(data, FIRSTSOCKET, buf, len, &nread); + if(err == CURLE_AGAIN) { + SET_SOCKERRNO(SOCKEWOULDBLOCK); + } + ret = err ? -1 : (ber_slen_t)nread; + } + } + return ret; +} + static ber_slen_t ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len); -static int ldapsb_tls_close(Sockbuf_IO_Desc *sbiod); + ber_len_t len) +{ + struct Curl_easy *data = sbiod->sbiod_pvt; + ber_slen_t ret = 0; + if(data) { + struct connectdata *conn = data->conn; + if(conn) { + struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + CURLcode err = CURLE_SEND_ERROR; + size_t nwritten; + + if(!li) { + SET_SOCKERRNO(SOCKEINVAL); + return -1; + } + err = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten); + if(err == CURLE_AGAIN) { + SET_SOCKERRNO(SOCKEWOULDBLOCK); + } + ret = err ? -1 : (ber_slen_t)nwritten; + } + } + return ret; +} static Sockbuf_IO ldapsb_tls = { ldapsb_tls_setup, @@ -585,6 +567,20 @@ static void oldap_conn_dtor(void *key, size_t klen, void *entry) curlx_free(li); } +/* SASL parameters for the ldap protocol */ +static const struct SASLproto saslldap = { + "ldap", /* The service name */ + oldap_perform_auth, /* Send authentication command */ + oldap_continue_auth, /* Send authentication continuation */ + oldap_cancel_auth, /* Send authentication cancellation */ + oldap_get_message, /* Get SASL response message */ + 0, /* Maximum initial response length (no max) */ + LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ + LDAP_SUCCESS, /* Code to receive upon authentication success */ + SASL_AUTH_NONE, /* Default mechanisms */ + 0 /* Configuration flags */ +}; + static CURLcode oldap_connect(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; @@ -619,7 +615,7 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) goto out; hosturl = curl_maprintf("%s://%s%s%s:%d", - conn->handler->scheme, + conn->scheme->name, conn->bits.ipv6_ip ? "[" : "", conn->host.name, conn->bits.ipv6_ip ? "]" : "", @@ -727,8 +723,9 @@ static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, if(bvals) { for(i = 0; bvals[i].bv_val; i++) { size_t llen; - unsigned short mech = Curl_sasl_decode_mech((char *)bvals[i].bv_val, - bvals[i].bv_len, &llen); + unsigned short mech = + Curl_sasl_decode_mech((const char *)bvals[i].bv_val, + bvals[i].bv_len, &llen); if(bvals[i].bv_len == llen) li->sasl.authmechs |= mech; } @@ -963,8 +960,7 @@ static CURLcode oldap_disconnect(struct Curl_easy *data, #ifdef USE_SSL if(ssl_installed(conn)) { Sockbuf *sb; - if((ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS) - || + if(ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS || ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data)) return CURLE_FAILED_INIT; } @@ -1242,88 +1238,6 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, return result; } -#ifdef USE_SSL -static int ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) -{ - sbiod->sbiod_pvt = arg; - return 0; -} - -static int ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) -{ - sbiod->sbiod_pvt = NULL; - return 0; -} - -/* We do not need to do anything because libcurl does it already */ -static int ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) -{ - (void)sbiod; - return 0; -} - -static int ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) -{ - (void)arg; - if(opt == LBER_SB_OPT_DATA_READY) { - struct Curl_easy *data = sbiod->sbiod_pvt; - return Curl_conn_data_pending(data, FIRSTSOCKET); - } - return 0; -} - -static ber_slen_t ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len) -{ - struct Curl_easy *data = sbiod->sbiod_pvt; - ber_slen_t ret = 0; - if(data) { - struct connectdata *conn = data->conn; - if(conn) { - struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - CURLcode err = CURLE_RECV_ERROR; - size_t nread; - - if(!li) { - SET_SOCKERRNO(SOCKEINVAL); - return -1; - } - err = (li->recv)(data, FIRSTSOCKET, buf, len, &nread); - if(err == CURLE_AGAIN) { - SET_SOCKERRNO(SOCKEWOULDBLOCK); - } - ret = err ? -1 : (ber_slen_t)nread; - } - } - return ret; -} -static ber_slen_t ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len) -{ - struct Curl_easy *data = sbiod->sbiod_pvt; - ber_slen_t ret = 0; - if(data) { - struct connectdata *conn = data->conn; - if(conn) { - struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - CURLcode err = CURLE_SEND_ERROR; - size_t nwritten; - - if(!li) { - SET_SOCKERRNO(SOCKEINVAL); - return -1; - } - err = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten); - if(err == CURLE_AGAIN) { - SET_SOCKERRNO(SOCKEWOULDBLOCK); - } - ret = err ? -1 : (ber_slen_t)nwritten; - } - } - return ret; -} -#endif /* USE_SSL */ - void Curl_ldap_version(char *buf, size_t bufsz) { LDAPAPIInfo api; @@ -1333,7 +1247,7 @@ void Curl_ldap_version(char *buf, size_t bufsz) unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); unsigned int minor = - (((unsigned int)api.ldapai_vendor_version - major * 10000) + (((unsigned int)api.ldapai_vendor_version - (major * 10000)) - patch) / 100; curl_msnprintf(buf, bufsz, "%s/%u.%u.%u", api.ldapai_vendor_name, major, minor, patch); @@ -1344,4 +1258,29 @@ void Curl_ldap_version(char *buf, size_t bufsz) curl_msnprintf(buf, bufsz, "OpenLDAP"); } +/* + * LDAP protocol handler. + */ +const struct Curl_protocol Curl_protocol_ldap = { + oldap_setup_connection, /* setup_connection */ + oldap_do, /* do_it */ + oldap_done, /* done */ + ZERO_NULL, /* do_more */ + oldap_connect, /* connect_it */ + oldap_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + oldap_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */ + +/* The LDAP scheme structs are in ldap.c */ diff --git a/vendor/curl/lib/parsedate.c b/vendor/curl/lib/parsedate.c index 9b4c483..7efedec 100644 --- a/vendor/curl/lib/parsedate.c +++ b/vendor/curl/lib/parsedate.c @@ -80,26 +80,6 @@ */ -/* - * parsedate() - * - * Returns: - * - * PARSEDATE_OK - a fine conversion - * PARSEDATE_FAIL - failed to convert - * PARSEDATE_LATER - time overflow at the far end of time_t - * PARSEDATE_SOONER - time underflow at the low end of time_t - */ - -static int parsedate(const char *date, time_t *output); - -#define PARSEDATE_OK 0 -#define PARSEDATE_FAIL -1 -#define PARSEDATE_LATER 1 -#if defined(HAVE_TIME_T_UNSIGNED) || (SIZEOF_TIME_T < 5) -#define PARSEDATE_SOONER 2 -#endif - #if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \ !defined(CURL_DISABLE_FILE) || defined(USE_GNUTLS) /* These names are also used by FTP and FILE code */ @@ -112,7 +92,16 @@ const char * const Curl_month[] = { }; #endif +#define PARSEDATE_OK 0 +#define PARSEDATE_FAIL (-1) + #ifndef CURL_DISABLE_PARSEDATE + +#define PARSEDATE_LATER 1 +#if defined(HAVE_TIME_T_UNSIGNED) || (SIZEOF_TIME_T < 5) +#define PARSEDATE_SOONER 2 +#endif + static const char * const weekday[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; @@ -124,84 +113,84 @@ struct tzinfo { /* Here's a bunch of frequently used time zone names. These were supported by the old getdate parser. */ -#define tDAYZONE -60 /* offset for daylight savings time */ +#define tDAYZONE (-60) /* offset for daylight savings time */ static const struct tzinfo tz[] = { - { "GMT", 0 }, /* Greenwich Mean */ - { "UT", 0 }, /* Universal Time */ - { "UTC", 0 }, /* Universal (Coordinated) */ - { "WET", 0 }, /* Western European */ - { "BST", 0 tDAYZONE }, /* British Summer */ - { "WAT", 60 }, /* West Africa */ - { "AST", 240 }, /* Atlantic Standard */ - { "ADT", 240 tDAYZONE }, /* Atlantic Daylight */ - { "EST", 300 }, /* Eastern Standard */ - { "EDT", 300 tDAYZONE }, /* Eastern Daylight */ - { "CST", 360 }, /* Central Standard */ - { "CDT", 360 tDAYZONE }, /* Central Daylight */ - { "MST", 420 }, /* Mountain Standard */ - { "MDT", 420 tDAYZONE }, /* Mountain Daylight */ - { "PST", 480 }, /* Pacific Standard */ - { "PDT", 480 tDAYZONE }, /* Pacific Daylight */ - { "YST", 540 }, /* Yukon Standard */ - { "YDT", 540 tDAYZONE }, /* Yukon Daylight */ - { "HST", 600 }, /* Hawaii Standard */ - { "HDT", 600 tDAYZONE }, /* Hawaii Daylight */ - { "CAT", 600 }, /* Central Alaska */ - { "AHST", 600 }, /* Alaska-Hawaii Standard */ - { "NT", 660 }, /* Nome */ /* spellchecker:disable-line */ - { "IDLW", 720 }, /* International Date Line West */ - { "CET", -60 }, /* Central European */ - { "MET", -60 }, /* Middle European */ - { "MEWT", -60 }, /* Middle European Winter */ - { "MEST", -60 tDAYZONE }, /* Middle European Summer */ - { "CEST", -60 tDAYZONE }, /* Central European Summer */ - { "MESZ", -60 tDAYZONE }, /* Middle European Summer */ - { "FWT", -60 }, /* French Winter */ - { "FST", -60 tDAYZONE }, /* French Summer */ - { "EET", -120 }, /* Eastern Europe, USSR Zone 1 */ + { "GMT", 0 }, /* Greenwich Mean */ + { "UT", 0 }, /* Universal Time */ + { "UTC", 0 }, /* Universal (Coordinated) */ + { "WET", 0 }, /* Western European */ + { "BST", 0 + tDAYZONE }, /* British Summer */ + { "WAT", 60 }, /* West Africa */ + { "AST", 240 }, /* Atlantic Standard */ + { "ADT", 240 + tDAYZONE }, /* Atlantic Daylight */ + { "EST", 300 }, /* Eastern Standard */ + { "EDT", 300 + tDAYZONE }, /* Eastern Daylight */ + { "CST", 360 }, /* Central Standard */ + { "CDT", 360 + tDAYZONE }, /* Central Daylight */ + { "MST", 420 }, /* Mountain Standard */ + { "MDT", 420 + tDAYZONE }, /* Mountain Daylight */ + { "PST", 480 }, /* Pacific Standard */ + { "PDT", 480 + tDAYZONE }, /* Pacific Daylight */ + { "YST", 540 }, /* Yukon Standard */ + { "YDT", 540 + tDAYZONE }, /* Yukon Daylight */ + { "HST", 600 }, /* Hawaii Standard */ + { "HDT", 600 + tDAYZONE }, /* Hawaii Daylight */ + { "CAT", 600 }, /* Central Alaska */ + { "AHST", 600 }, /* Alaska-Hawaii Standard */ + { "NT", 660 }, /* Nome */ /* spellchecker:disable-line */ + { "IDLW", 720 }, /* International Date Line West */ + { "CET", -60 }, /* Central European */ + { "MET", -60 }, /* Middle European */ + { "MEWT", -60 }, /* Middle European Winter */ + { "MEST", -60 + tDAYZONE }, /* Middle European Summer */ + { "CEST", -60 + tDAYZONE }, /* Central European Summer */ + { "MESZ", -60 + tDAYZONE }, /* Middle European Summer */ + { "FWT", -60 }, /* French Winter */ + { "FST", -60 + tDAYZONE }, /* French Summer */ + { "EET", -120 }, /* Eastern Europe, USSR Zone 1 */ { "WAST", -420 }, /* spellchecker:disable-line */ - /* West Australian Standard */ - { "WADT", -420 tDAYZONE }, /* West Australian Daylight */ - { "CCT", -480 }, /* China Coast, USSR Zone 7 */ - { "JST", -540 }, /* Japan Standard, USSR Zone 8 */ - { "EAST", -600 }, /* Eastern Australian Standard */ - { "EADT", -600 tDAYZONE }, /* Eastern Australian Daylight */ - { "GST", -600 }, /* Guam Standard, USSR Zone 9 */ - { "NZT", -720 }, /* New Zealand */ - { "NZST", -720 }, /* New Zealand Standard */ - { "NZDT", -720 tDAYZONE }, /* New Zealand Daylight */ - { "IDLE", -720 }, /* International Date Line East */ + /* West Australian Standard */ + { "WADT", -420 + tDAYZONE }, /* West Australian Daylight */ + { "CCT", -480 }, /* China Coast, USSR Zone 7 */ + { "JST", -540 }, /* Japan Standard, USSR Zone 8 */ + { "EAST", -600 }, /* Eastern Australian Standard */ + { "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */ + { "GST", -600 }, /* Guam Standard, USSR Zone 9 */ + { "NZT", -720 }, /* New Zealand */ + { "NZST", -720 }, /* New Zealand Standard */ + { "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */ + { "IDLE", -720 }, /* International Date Line East */ /* Next up: Military timezone names. RFC822 allowed these, but (as noted in RFC 1123) had their signs wrong. Here we use the correct signs to match actual military usage. */ - { "A", 1 * 60 }, /* Alpha */ - { "B", 2 * 60 }, /* Bravo */ - { "C", 3 * 60 }, /* Charlie */ - { "D", 4 * 60 }, /* Delta */ - { "E", 5 * 60 }, /* Echo */ - { "F", 6 * 60 }, /* Foxtrot */ - { "G", 7 * 60 }, /* Golf */ - { "H", 8 * 60 }, /* Hotel */ - { "I", 9 * 60 }, /* India */ + { "A", 1 * 60 }, /* Alpha */ + { "B", 2 * 60 }, /* Bravo */ + { "C", 3 * 60 }, /* Charlie */ + { "D", 4 * 60 }, /* Delta */ + { "E", 5 * 60 }, /* Echo */ + { "F", 6 * 60 }, /* Foxtrot */ + { "G", 7 * 60 }, /* Golf */ + { "H", 8 * 60 }, /* Hotel */ + { "I", 9 * 60 }, /* India */ /* "J", Juliet is not used as a timezone, to indicate the observer's local time */ - { "K", 10 * 60 }, /* Kilo */ - { "L", 11 * 60 }, /* Lima */ - { "M", 12 * 60 }, /* Mike */ - { "N", -1 * 60 }, /* November */ - { "O", -2 * 60 }, /* Oscar */ - { "P", -3 * 60 }, /* Papa */ - { "Q", -4 * 60 }, /* Quebec */ - { "R", -5 * 60 }, /* Romeo */ - { "S", -6 * 60 }, /* Sierra */ - { "T", -7 * 60 }, /* Tango */ - { "U", -8 * 60 }, /* Uniform */ - { "V", -9 * 60 }, /* Victor */ - { "W", -10 * 60 }, /* Whiskey */ - { "X", -11 * 60 }, /* X-ray */ - { "Y", -12 * 60 }, /* Yankee */ - { "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */ + { "K", 10 * 60 }, /* Kilo */ + { "L", 11 * 60 }, /* Lima */ + { "M", 12 * 60 }, /* Mike */ + { "N", -1 * 60 }, /* November */ + { "O", -2 * 60 }, /* Oscar */ + { "P", -3 * 60 }, /* Papa */ + { "Q", -4 * 60 }, /* Quebec */ + { "R", -5 * 60 }, /* Romeo */ + { "S", -6 * 60 }, /* Sierra */ + { "T", -7 * 60 }, /* Tango */ + { "U", -8 * 60 }, /* Uniform */ + { "V", -9 * 60 }, /* Victor */ + { "W", -10 * 60 }, /* Whiskey */ + { "X", -11 * 60 }, /* X-ray */ + { "Y", -12 * 60 }, /* Yankee */ + { "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */ }; /* returns: @@ -290,9 +279,10 @@ static time_t time2epoch(int sec, int min, int hour, int leap_days = year - (mon <= 1); leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400) - (1969 / 4) + (1969 / 100) - (1969 / 400)); - return ((((time_t)(year - 1970) * 365 - + leap_days + month_days_cumulative[mon] + mday - 1) * 24 - + hour) * 60 + min) * 60 + sec; + return ((((((((time_t)(year - 1970) * 365) + + leap_days + month_days_cumulative[mon] + mday - 1) * 24) + + hour) * 60) + + min) * 60) + sec; } /* Returns the value of a single-digit or two-digit decimal number, return @@ -303,7 +293,7 @@ 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'); + return (num * 10) + (date[1] - '0'); } *endp = &date[1]; return num; @@ -397,7 +387,7 @@ static int parsedate(const char *date, time_t *output) } if(!found && (tzoff == -1)) { - /* this just must be a time zone string */ + /* this must be a time zone string */ tzoff = checktz(date, len); if(tzoff != -1) found = TRUE; @@ -432,7 +422,7 @@ static int parsedate(const char *date, time_t *output) (num_digits == 4) && (val <= 1400) && (indate < date) && - ((date[-1] == '+' || date[-1] == '-'))) { + (date[-1] == '+' || date[-1] == '-')) { /* four digits and a value less than or equal to 1400 (to take into account all sorts of funny time zone diffs) and it is preceded with a plus or minus. This is a time zone indication. 1400 is @@ -442,7 +432,7 @@ static int parsedate(const char *date, time_t *output) anyone has a more authoritative source for the exact maximum time zone offsets, please speak up! */ found = TRUE; - tzoff = (val / 100 * 60 + val % 100) * 60; + tzoff = ((val / 100 * 60) + (val % 100)) * 60; /* the + and - prefix indicates the local time compared to GMT, this we need their reversed math to get what we want */ @@ -456,7 +446,7 @@ static int parsedate(const char *date, time_t *output) /* 8 digits, no year, month or day yet. This is YYYYMMDD */ found = TRUE; yearnum = val / 10000; - monnum = (val % 10000) / 100 - 1; /* month is 0 - 11 */ + monnum = ((val % 10000) / 100) - 1; /* month is 0 - 11 */ mdaynum = val % 100; } @@ -491,12 +481,12 @@ static int parsedate(const char *date, time_t *output) part++; } - if(-1 == secnum) + if(secnum == -1) secnum = minnum = hournum = 0; /* no time, make it zero */ - if((-1 == mdaynum) || - (-1 == monnum) || - (-1 == yearnum)) + if((mdaynum == -1) || + (monnum == -1) || + (yearnum == -1)) /* lacks vital info, fail */ return PARSEDATE_FAIL; @@ -569,11 +559,11 @@ static int parsedate(const char *date, time_t *output) } #endif -time_t curl_getdate(const char *p, const time_t *now) +time_t curl_getdate(const char *p, const time_t *unused) { time_t parsed = -1; int rc = parsedate(p, &parsed); - (void)now; /* legacy argument from the past that we ignore */ + (void)unused; /* legacy argument from the past that we ignore */ if(rc == PARSEDATE_OK) { if(parsed == (time_t)-1) diff --git a/vendor/curl/lib/parsedate.h b/vendor/curl/lib/parsedate.h index a6ee43a..ea136bd 100644 --- a/vendor/curl/lib/parsedate.h +++ b/vendor/curl/lib/parsedate.h @@ -30,6 +30,6 @@ extern const char * const Curl_month[12]; TIME_T_MAX in case the parsed time value was too big, instead of an error. */ -int Curl_getdate_capped(const char *p, time_t *store); +int Curl_getdate_capped(const char *p, time_t *tp); #endif /* HEADER_CURL_PARSEDATE_H */ diff --git a/vendor/curl/lib/pingpong.c b/vendor/curl/lib/pingpong.c index 5af5305..4ede0a5 100644 --- a/vendor/curl/lib/pingpong.c +++ b/vendor/curl/lib/pingpong.c @@ -27,22 +27,24 @@ #include "curl_setup.h" #include "urldata.h" +#include "pingpong.h" + +#ifdef USE_PINGPONG + #include "cfilters.h" #include "connect.h" +#include "multiif.h" #include "sendf.h" #include "curl_trc.h" #include "select.h" #include "progress.h" -#include "pingpong.h" - -#ifdef USE_PINGPONG /* Returns timeout in ms. 0 or negative number means the timeout has already triggered */ timediff_t Curl_pp_state_timeout(struct Curl_easy *data, - struct pingpong *pp, bool disconnecting) + struct pingpong *pp) { - timediff_t timeout_ms; /* in milliseconds */ + timediff_t timeout_ms, xfer_timeout_ms; timediff_t response_time = data->set.server_response_timeout ? data->set.server_response_timeout : RESP_TIMEOUT; @@ -55,19 +57,10 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data, full response to arrive before we bail out */ timeout_ms = response_time - curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response); - - if(data->set.timeout && !disconnecting) { - /* if timeout is requested, find out how much overall remains */ - timediff_t timeout2_ms = Curl_timeleft_ms(data, FALSE); - /* pick the lowest number */ - timeout_ms = CURLMIN(timeout_ms, timeout2_ms); - } - - if(disconnecting) { - timediff_t total_left_ms = Curl_timeleft_ms(data, FALSE); - timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0)); - } - + /* transfer timeout can be 0, which means no timeout applies */ + xfer_timeout_ms = Curl_timeleft_ms(data); + if(xfer_timeout_ms && (xfer_timeout_ms < timeout_ms)) + return xfer_timeout_ms; return timeout_ms; } @@ -82,7 +75,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc; timediff_t interval_ms; - timediff_t timeout_ms = Curl_pp_state_timeout(data, pp, disconnecting); + timediff_t timeout_ms = Curl_pp_state_timeout(data, pp); CURLcode result = CURLE_OK; if(timeout_ms <= 0) { @@ -101,7 +94,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, if(Curl_conn_data_pending(data, FIRSTSOCKET)) rc = 1; else if(pp->overflow) - /* We are receiving and there is data in the cache so just read it */ + /* We are receiving and there is data in the cache so read it */ rc = 1; else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET)) /* We are receiving and there is data ready in the SSL library */ @@ -300,8 +293,8 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, } do { - char *line = curlx_dyn_ptr(&pp->recvbuf); - char *nl = memchr(line, '\n', curlx_dyn_len(&pp->recvbuf)); + const char *line = curlx_dyn_ptr(&pp->recvbuf); + const char *nl = memchr(line, '\n', curlx_dyn_len(&pp->recvbuf)); if(nl) { /* a newline is CRLF in pp-talk, so the CR is ignored as the line is not really terminated until the LF comes */ diff --git a/vendor/curl/lib/pingpong.h b/vendor/curl/lib/pingpong.h index a7292e6..33d2a92 100644 --- a/vendor/curl/lib/pingpong.h +++ b/vendor/curl/lib/pingpong.h @@ -91,7 +91,7 @@ void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow); /* Returns timeout in ms. 0 or negative number means the timeout has already triggered */ timediff_t Curl_pp_state_timeout(struct Curl_easy *data, - struct pingpong *pp, bool disconnecting); + struct pingpong *pp); /*********************************************************************** * diff --git a/vendor/curl/lib/pop3.c b/vendor/curl/lib/pop3.c index 8e2c8aa..de704d1 100644 --- a/vendor/curl/lib/pop3.c +++ b/vendor/curl/lib/pop3.c @@ -37,6 +37,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "pop3.h" #ifndef CURL_DISABLE_POP3 @@ -54,7 +56,6 @@ #include #endif -#include "urldata.h" #include "sendf.h" #include "curl_trc.h" #include "hostip.h" @@ -62,7 +63,6 @@ #include "transfer.h" #include "escape.h" #include "pingpong.h" -#include "pop3.h" #include "vtls/vtls.h" #include "cfilters.h" #include "connect.h" @@ -71,7 +71,7 @@ #include "bufref.h" #include "curl_sasl.h" #include "curl_md5.h" -#include "strdup.h" +#include "curlx/strdup.h" /* Authentication type flags */ #define POP3_TYPE_CLEARTEXT (1 << 0) @@ -135,111 +135,6 @@ struct pop3_conn { BIT(tls_supported); /* StartTLS capability supported by server */ }; -/* Local API functions */ -static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done); -static CURLcode pop3_do(struct Curl_easy *data, bool *done); -static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode pop3_connect(struct Curl_easy *data, bool *done); -static CURLcode pop3_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode pop3_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode pop3_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode pop3_parse_url_options(struct connectdata *conn); -static CURLcode pop3_parse_url_path(struct Curl_easy *data); -static CURLcode pop3_parse_custom_request(struct Curl_easy *data); -static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out); - -/* This function scans the body after the end-of-body and writes everything - * until the end is found */ -static CURLcode pop3_write(struct Curl_easy *data, - const char *str, size_t nread, bool is_eos); - -/* - * POP3 protocol handler. - */ - -const struct Curl_handler Curl_handler_pop3 = { - "pop3", /* scheme */ - pop3_setup_connection, /* setup_connection */ - pop3_do, /* do_it */ - pop3_done, /* done */ - ZERO_NULL, /* do_more */ - pop3_connect, /* connect_it */ - pop3_multi_statemach, /* connecting */ - pop3_doing, /* doing */ - pop3_pollset, /* proto_pollset */ - pop3_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - pop3_disconnect, /* disconnect */ - pop3_write, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_POP3, /* defport */ - CURLPROTO_POP3, /* protocol */ - CURLPROTO_POP3, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE -}; - -#ifdef USE_SSL -/* - * POP3S protocol handler. - */ - -const struct Curl_handler Curl_handler_pop3s = { - "pop3s", /* scheme */ - pop3_setup_connection, /* setup_connection */ - pop3_do, /* do_it */ - pop3_done, /* done */ - ZERO_NULL, /* do_more */ - pop3_connect, /* connect_it */ - pop3_multi_statemach, /* connecting */ - pop3_doing, /* doing */ - pop3_pollset, /* proto_pollset */ - pop3_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - pop3_disconnect, /* disconnect */ - pop3_write, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_POP3S, /* defport */ - CURLPROTO_POP3S, /* protocol */ - CURLPROTO_POP3, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE -}; -#endif - -/* SASL parameters for the pop3 protocol */ -static const struct SASLproto saslpop3 = { - "pop", /* The service name */ - pop3_perform_auth, /* Send authentication command */ - pop3_continue_auth, /* Send authentication continuation */ - pop3_cancel_auth, /* Send authentication cancellation */ - pop3_get_message, /* Get SASL response message */ - 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ - '*', /* Code received when continuation is expected */ - '+', /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ -}; - struct pop3_cmd { const char *name; unsigned short nlen; @@ -268,6 +163,105 @@ static const struct pop3_cmd pop3cmds[] = { { "XTND", 4, TRUE, TRUE }, }; +/*********************************************************************** + * + * pop3_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode pop3_parse_url_options(struct connectdata *conn) +{ + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + CURLcode result = CURLE_OK; + const char *ptr = conn->options; + + if(!pop3c) + return CURLE_FAILED_INIT; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(curl_strnequal(key, "AUTH=", 5)) { + result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, + value, ptr - value); + + if(result && curl_strnequal(value, "+APOP", ptr - value)) { + pop3c->preftype = POP3_TYPE_APOP; + pop3c->sasl.prefmech = SASL_AUTH_NONE; + result = CURLE_OK; + } + } + else + result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; + } + + if(pop3c->preftype != POP3_TYPE_APOP) + switch(pop3c->sasl.prefmech) { + case SASL_AUTH_NONE: + pop3c->preftype = POP3_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + pop3c->preftype = POP3_TYPE_ANY; + break; + default: + pop3c->preftype = POP3_TYPE_SASL; + break; + } + + return result; +} + +/*********************************************************************** + * + * pop3_parse_url_path() + * + * Parse the URL path into separate path components. + */ +static CURLcode pop3_parse_url_path(struct Curl_easy *data) +{ + /* The POP3 struct is already initialised in pop3_connect() */ + struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); + const char *path = &data->state.up.path[1]; /* skip leading path */ + + if(!pop3) + return CURLE_FAILED_INIT; + /* URL decode the path for the message ID */ + return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL); +} + +/*********************************************************************** + * + * pop3_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode pop3_parse_custom_request(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + if(!pop3) + return CURLE_FAILED_INIT; + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL); + + return result; +} + /* Return iff a command is defined as "multi-line" (RFC 1939), * has a response terminated by a last line with a '.'. */ @@ -277,9 +271,9 @@ static bool pop3_is_multiline(const char *cmdline) for(i = 0; i < CURL_ARRAYSIZE(pop3cmds); ++i) { if(curl_strnequal(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) { if(!cmdline[pop3cmds[i].nlen]) - return pop3cmds[i].multiline; + return (bool)pop3cmds[i].multiline; else if(cmdline[pop3cmds[i].nlen] == ' ') - return pop3cmds[i].multiline_with_args; + return (bool)pop3cmds[i].multiline_with_args; } } /* Unknown command, assume multi-line for backward compatibility with @@ -364,13 +358,12 @@ static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out) if(len > 2) { /* Find the start of the message */ len -= 2; - for(message += 2; *message == ' ' || *message == '\t'; message++, len--) + for(message += 2; ISBLANK(*message); message++, len--) ; /* Find the end of the message */ while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') + if(!ISBLANK(message[len]) && !ISNEWLINE(message[len])) break; /* Terminate the message */ @@ -395,7 +388,7 @@ static void pop3_state(struct Curl_easy *data, pop3state newstate) struct pop3_conn *pop3c = Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); if(pop3c) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* for debug purposes */ static const char * const names[] = { "STOP", @@ -496,13 +489,13 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, if(result) goto out; /* Change the connection handler */ - conn->handler = &Curl_handler_pop3s; + conn->scheme = &Curl_scheme_pop3s; } DEBUGASSERT(!pop3c->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + result, ssldone)); if(!result && ssldone) { pop3c->ssldone = ssldone; /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */ @@ -564,7 +557,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, size_t i; struct MD5_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; - char secret[2 * MD5_DIGEST_LEN + 1]; + char secret[(2 * MD5_DIGEST_LEN) + 1]; if(!pop3c) return CURLE_FAILED_INIT; @@ -816,8 +809,8 @@ static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data, } else if(len > 3) { /* Does the server support APOP authentication? */ - char *lt; - char *gt = NULL; + const char *lt; + const char *gt = NULL; /* Look for the APOP timestamp */ lt = memchr(line, '<', len); @@ -827,13 +820,13 @@ static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data, if(gt) { /* the length of the timestamp, including the brackets */ size_t timestamplen = gt - lt + 1; - char *at = memchr(lt, '@', timestamplen); + const char *at = memchr(lt, '@', timestamplen); /* If the timestamp does not contain '@' it is not (as required by RFC-1939) conformant to the RFC-822 message id syntax, and we therefore do not use APOP authentication. */ if(at) { /* dupe the timestamp */ - pop3c->apoptimestamp = Curl_memdup0(lt, timestamplen); + pop3c->apoptimestamp = curlx_memdup0(lt, timestamplen); if(!pop3c->apoptimestamp) return CURLE_OUT_OF_MEMORY; /* Store the APOP capability */ @@ -1070,59 +1063,199 @@ static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code, return result; } -/* For command responses */ -static CURLcode pop3_state_command_resp(struct Curl_easy *data, - int pop3code, - pop3state instate) +/*********************************************************************** + * + * pop3_write() + * + * This function scans the body after the end-of-body and writes everything + * until the end is found. + */ +static CURLcode pop3_write(struct Curl_easy *data, const char *str, + size_t nread, bool is_eos) { + /* This code could be made into a special function in the handler struct */ CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; struct connectdata *conn = data->conn; - struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - struct pingpong *pp; + bool strip_dot = FALSE; + size_t last = 0; + size_t i; + (void)is_eos; - (void)instate; - if(!pop3 || !pop3c) + if(!pop3c) return CURLE_FAILED_INIT; - pp = &pop3c->pp; - if(pop3code != '+') { - pop3_state(data, POP3_STOP); - return CURLE_WEIRD_SERVER_REPLY; - } - - /* This 'OK' line ends with a CR LF pair which is the two first bytes of the - EOB string so count this is two matching bytes. This is necessary to make - the code detect the EOB if the only data than comes now is %2e CR LF like - when there is no body to return. */ - pop3c->eob = 2; - - /* But since this initial CR LF pair is not part of the actual body, we set - the strip counter here so that these bytes will not be delivered. */ - pop3c->strip = 2; + /* Search through the buffer looking for the end-of-body marker which is + 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches + the eob so the server will have prefixed it with an extra dot which we + need to strip out. Additionally the marker could of course be spread out + over 5 different data chunks. */ + for(i = 0; i < nread; i++) { + size_t prev = pop3c->eob; - if(pop3->transfer == PPTRANSFER_BODY) { - /* POP3 download */ - Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); + switch(str[i]) { + case 0x0d: + if(pop3c->eob == 0) { + pop3c->eob++; - if(pp->overflow) { - /* The recv buffer contains data that is actually body content so send - it as such. Note that there may even be additional "headers" after - the body */ + if(i) { + /* Write out the body part that did not match */ + result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], + i - last); - /* keep only the overflow */ - curlx_dyn_tail(&pp->recvbuf, pp->overflow); - pp->nfinal = 0; /* done */ + if(result) + return result; - if(!data->req.no_body) { - result = pop3_write(data, curlx_dyn_ptr(&pp->recvbuf), - curlx_dyn_len(&pp->recvbuf), FALSE); - if(result) - return result; + last = i; + } } - - /* reset the buffer */ - curlx_dyn_reset(&pp->recvbuf); + else if(pop3c->eob == 3) + pop3c->eob++; + else + /* If the character match was not at position 0 or 3 then restart the + pattern matching */ + pop3c->eob = 1; + break; + + case 0x0a: + if(pop3c->eob == 1 || pop3c->eob == 4) + pop3c->eob++; + else + /* If the character match was not at position 1 or 4 then start the + search again */ + pop3c->eob = 0; + break; + + case 0x2e: + if(pop3c->eob == 2) + pop3c->eob++; + else if(pop3c->eob == 3) { + /* We have an extra dot after the CRLF which we need to strip off */ + strip_dot = TRUE; + pop3c->eob = 0; + } + else + /* If the character match was not at position 2 then start the search + again */ + pop3c->eob = 0; + break; + + default: + pop3c->eob = 0; + break; + } + + /* Did we have a partial match which has subsequently failed? */ + if(prev && prev >= pop3c->eob) { + /* Strip can only be non-zero for the first mismatch after CRLF and + then both prev and strip are equal and nothing will be output below */ + while(prev && pop3c->strip) { + prev--; + pop3c->strip--; + } + + if(prev) { + /* If the partial match was the CRLF and dot then only write the CRLF + as the server would have inserted the dot */ + if(strip_dot && prev - 1 > 0) { + result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, + prev - 1); + } + else if(!strip_dot) { + result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, + prev); + } + else { + result = CURLE_OK; + } + + if(result) + return result; + + last = i; + strip_dot = FALSE; + } + } + } + + if(pop3c->eob == POP3_EOB_LEN) { + /* We have a full match so the transfer is done, however we must transfer + the CRLF at the start of the EOB as this is considered to be part of the + message as per RFC-1939, sect. 3 */ + result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2); + + k->keepon &= ~KEEP_RECV; + pop3c->eob = 0; + + return result; + } + + if(pop3c->eob) + /* While EOB is matching nothing should be output */ + return CURLE_OK; + + if(nread - last) { + result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], + nread - last); + } + + return result; +} + +/* For command responses */ +static CURLcode pop3_state_command_resp(struct Curl_easy *data, + int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); + struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); + struct pingpong *pp; + + (void)instate; + if(!pop3 || !pop3c) + return CURLE_FAILED_INIT; + + pp = &pop3c->pp; + if(pop3code != '+') { + pop3_state(data, POP3_STOP); + return CURLE_WEIRD_SERVER_REPLY; + } + + /* This 'OK' line ends with a CR LF pair which is the two first bytes of the + EOB string so count this is two matching bytes. This is necessary to make + the code detect the EOB if the only data than comes now is %2e CR LF like + when there is no body to return. */ + pop3c->eob = 2; + + /* But since this initial CR LF pair is not part of the actual body, we set + the strip counter here so that these bytes will not be delivered. */ + pop3c->strip = 2; + + if(pop3->transfer == PPTRANSFER_BODY) { + /* POP3 download */ + Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); + + if(pp->overflow) { + /* The recv buffer contains data that is actually body content so send + it as such. Note that there may even be additional "headers" after + the body */ + + /* keep only the overflow */ + curlx_dyn_tail(&pp->recvbuf, pp->overflow); + pp->nfinal = 0; /* done */ + + if(!data->req.no_body) { + result = pop3_write(data, curlx_dyn_ptr(&pp->recvbuf), + curlx_dyn_len(&pp->recvbuf), FALSE); + if(result) + return result; + } + + /* reset the buffer */ + curlx_dyn_reset(&pp->recvbuf); pp->overflow = 0; } } @@ -1161,11 +1294,11 @@ static CURLcode pop3_statemachine(struct Curl_easy *data, if(pp->sendleft) return Curl_pp_flushsend(data, pp); - do { - /* Read the response from the server */ - result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread); - if(result) - return result; + do { + /* Read the response from the server */ + result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread); + if(result) + return result; if(!pop3code) break; @@ -1264,6 +1397,20 @@ static CURLcode pop3_pollset(struct Curl_easy *data, return pop3c ? Curl_pp_pollset(data, &pop3c->pp, ps) : CURLE_OK; } +/* SASL parameters for the pop3 protocol */ +static const struct SASLproto saslpop3 = { + "pop", /* The service name */ + pop3_perform_auth, /* Send authentication command */ + pop3_continue_auth, /* Send authentication continuation */ + pop3_cancel_auth, /* Send authentication cancellation */ + pop3_get_message, /* Get SASL response message */ + 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + '*', /* Code received when continuation is expected */ + '+', /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + /*********************************************************************** * * pop3_connect() @@ -1382,6 +1529,46 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, return result; } +/* Call this when the DO phase has completed */ +static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected) +{ + (void)data; + (void)connected; + + return CURLE_OK; +} + +/*********************************************************************** + * + * pop3_regular_transfer() + * + * The input argument is already checked for validity. + * + * Performs all commands done before a regular transfer between a local and a + * remote host. + */ +static CURLcode pop3_regular_transfer(struct Curl_easy *data, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + + /* Make sure size is unknown at this point */ + data->req.size = -1; + + /* Set the progress data */ + Curl_pgrsReset(data); + + /* Carry out the perform */ + result = pop3_perform(data, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = pop3_dophase_done(data, connected); + + return result; +} + /*********************************************************************** * * pop3_do() @@ -1446,15 +1633,6 @@ static CURLcode pop3_disconnect(struct Curl_easy *data, return CURLE_OK; } -/* Call this when the DO phase has completed */ -static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected) -{ - (void)data; - (void)connected; - - return CURLE_OK; -} - /* Called from multi.c while DOing */ static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done) { @@ -1471,37 +1649,6 @@ static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done) return result; } -/*********************************************************************** - * - * pop3_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - */ -static CURLcode pop3_regular_transfer(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - - /* Make sure size is unknown at this point */ - data->req.size = -1; - - /* Set the progress data */ - Curl_pgrsReset(data); - - /* Carry out the perform */ - result = pop3_perform(data, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = pop3_dophase_done(data, connected); - - return result; -} - static void pop3_easy_dtor(void *key, size_t klen, void *entry) { struct POP3 *pop3 = entry; @@ -1542,243 +1689,61 @@ static CURLcode pop3_setup_connection(struct Curl_easy *data, return CURLE_OK; } -/*********************************************************************** - * - * pop3_parse_url_options() - * - * Parse the URL login options. +/* + * POP3 protocol. */ -static CURLcode pop3_parse_url_options(struct connectdata *conn) -{ - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - CURLcode result = CURLE_OK; - const char *ptr = conn->options; - - if(!pop3c) - return CURLE_FAILED_INIT; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(curl_strnequal(key, "AUTH=", 5)) { - result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, - value, ptr - value); - - if(result && curl_strnequal(value, "+APOP", ptr - value)) { - pop3c->preftype = POP3_TYPE_APOP; - pop3c->sasl.prefmech = SASL_AUTH_NONE; - result = CURLE_OK; - } - } - else - result = CURLE_URL_MALFORMAT; - - if(*ptr == ';') - ptr++; - } - - if(pop3c->preftype != POP3_TYPE_APOP) - switch(pop3c->sasl.prefmech) { - case SASL_AUTH_NONE: - pop3c->preftype = POP3_TYPE_NONE; - break; - case SASL_AUTH_DEFAULT: - pop3c->preftype = POP3_TYPE_ANY; - break; - default: - pop3c->preftype = POP3_TYPE_SASL; - break; - } - - return result; -} - -/*********************************************************************** - * - * pop3_parse_url_path() - * - * Parse the URL path into separate path components. - */ -static CURLcode pop3_parse_url_path(struct Curl_easy *data) -{ - /* The POP3 struct is already initialised in pop3_connect() */ - struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); - const char *path = &data->state.up.path[1]; /* skip leading path */ +static const struct Curl_protocol Curl_protocol_pop3 = { + pop3_setup_connection, /* setup_connection */ + pop3_do, /* do_it */ + pop3_done, /* done */ + ZERO_NULL, /* do_more */ + pop3_connect, /* connect_it */ + pop3_multi_statemach, /* connecting */ + pop3_doing, /* doing */ + pop3_pollset, /* proto_pollset */ + pop3_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + pop3_disconnect, /* disconnect */ + pop3_write, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; - if(!pop3) - return CURLE_FAILED_INIT; - /* URL decode the path for the message ID */ - return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL); -} +#endif /* CURL_DISABLE_POP3 */ -/*********************************************************************** - * - * pop3_parse_custom_request() - * - * Parse the custom request. +/* + * POP3 protocol handler. */ -static CURLcode pop3_parse_custom_request(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - - if(!pop3) - return CURLE_FAILED_INIT; - /* URL decode the custom request */ - if(custom) - result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL); - - return result; -} +const struct Curl_scheme Curl_scheme_pop3 = { + "pop3", /* scheme */ +#ifdef CURL_DISABLE_POP3 + ZERO_NULL, +#else + &Curl_protocol_pop3, +#endif + CURLPROTO_POP3, /* protocol */ + CURLPROTO_POP3, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE, + PORT_POP3, /* defport */ +}; -/*********************************************************************** - * - * pop3_write() - * - * This function scans the body after the end-of-body and writes everything - * until the end is found. +/* + * POP3S protocol handler. */ -static CURLcode pop3_write(struct Curl_easy *data, const char *str, - size_t nread, bool is_eos) -{ - /* This code could be made into a special function in the handler struct */ - CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); - bool strip_dot = FALSE; - size_t last = 0; - size_t i; - (void)is_eos; - - if(!pop3c) - return CURLE_FAILED_INIT; - - /* Search through the buffer looking for the end-of-body marker which is - 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches - the eob so the server will have prefixed it with an extra dot which we - need to strip out. Additionally the marker could of course be spread out - over 5 different data chunks. */ - for(i = 0; i < nread; i++) { - size_t prev = pop3c->eob; - - switch(str[i]) { - case 0x0d: - if(pop3c->eob == 0) { - pop3c->eob++; - - if(i) { - /* Write out the body part that did not match */ - result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], - i - last); - - if(result) - return result; - - last = i; - } - } - else if(pop3c->eob == 3) - pop3c->eob++; - else - /* If the character match was not at position 0 or 3 then restart the - pattern matching */ - pop3c->eob = 1; - break; - - case 0x0a: - if(pop3c->eob == 1 || pop3c->eob == 4) - pop3c->eob++; - else - /* If the character match was not at position 1 or 4 then start the - search again */ - pop3c->eob = 0; - break; - - case 0x2e: - if(pop3c->eob == 2) - pop3c->eob++; - else if(pop3c->eob == 3) { - /* We have an extra dot after the CRLF which we need to strip off */ - strip_dot = TRUE; - pop3c->eob = 0; - } - else - /* If the character match was not at position 2 then start the search - again */ - pop3c->eob = 0; - break; - - default: - pop3c->eob = 0; - break; - } - - /* Did we have a partial match which has subsequently failed? */ - if(prev && prev >= pop3c->eob) { - /* Strip can only be non-zero for the first mismatch after CRLF and - then both prev and strip are equal and nothing will be output below */ - while(prev && pop3c->strip) { - prev--; - pop3c->strip--; - } - - if(prev) { - /* If the partial match was the CRLF and dot then only write the CRLF - as the server would have inserted the dot */ - if(strip_dot && prev - 1 > 0) { - result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, - prev - 1); - } - else if(!strip_dot) { - result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, - prev); - } - else { - result = CURLE_OK; - } - - if(result) - return result; - - last = i; - strip_dot = FALSE; - } - } - } - - if(pop3c->eob == POP3_EOB_LEN) { - /* We have a full match so the transfer is done, however we must transfer - the CRLF at the start of the EOB as this is considered to be part of the - message as per RFC-1939, sect. 3 */ - result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2); - - k->keepon &= ~KEEP_RECV; - pop3c->eob = 0; - - return result; - } - - if(pop3c->eob) - /* While EOB is matching nothing should be output */ - return CURLE_OK; - - if(nread - last) { - result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], - nread - last); - } - - return result; -} - -#endif /* CURL_DISABLE_POP3 */ +const struct Curl_scheme Curl_scheme_pop3s = { + "pop3s", /* scheme */ +#if defined(CURL_DISABLE_POP3) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_pop3, +#endif + CURLPROTO_POP3S, /* protocol */ + CURLPROTO_POP3, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_POP3S, /* defport */ +}; diff --git a/vendor/curl/lib/pop3.h b/vendor/curl/lib/pop3.h index ed00dd1..77e30f8 100644 --- a/vendor/curl/lib/pop3.h +++ b/vendor/curl/lib/pop3.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_handler Curl_handler_pop3; -extern const struct Curl_handler Curl_handler_pop3s; +extern const struct Curl_scheme Curl_scheme_pop3; +extern const struct Curl_scheme Curl_scheme_pop3s; #endif /* HEADER_CURL_POP3_H */ diff --git a/vendor/curl/lib/progress.c b/vendor/curl/lib/progress.c index 9cbd267..6cde567 100644 --- a/vendor/curl/lib/progress.c +++ b/vendor/curl/lib/progress.c @@ -30,9 +30,6 @@ #include "transfer.h" #include "curlx/strcopy.h" -/* check rate limits within this many recent milliseconds, at minimum. */ -#define MIN_RATE_LIMIT_PERIOD 3000 - #ifndef CURL_DISABLE_PROGRESS_METER /* Provide a string that is 7 letters long (plus the zero byte). @@ -43,7 +40,7 @@ UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds) { curl_off_t h; if(seconds <= 0) { - curlx_strcopy(r, rsize, " ", 7); + curlx_strcopy(r, rsize, STRCONST(" ")); return; } h = seconds / 3600; @@ -76,7 +73,7 @@ UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds) if(y <= 99999) curl_msnprintf(r, rsize, "%6" FMT_OFF_T "y", y); else - curlx_strcopy(r, rsize, ">99999y", 7); + curlx_strcopy(r, rsize, STRCONST(">99999y")); } } } @@ -152,11 +149,9 @@ UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, if(howlong >= data->set.low_speed_time * 1000) { /* too long */ - failf(data, - "Operation too slow. " - "Less than %ld bytes/sec transferred the last %ld seconds", - data->set.low_speed_limit, - data->set.low_speed_time); + failf(data, "Operation too slow. Less than %" FMT_OFF_T + " bytes/sec transferred the last %u seconds", + data->set.low_speed_limit, data->set.low_speed_time); return CURLE_OPERATION_TIMEDOUT; } } @@ -182,7 +177,6 @@ const struct curltime *Curl_pgrs_now(struct Curl_easy *data) } /* - New proposed interface, 9th of February 2000: pgrsStartNow() - sets start time @@ -192,7 +186,6 @@ const struct curltime *Curl_pgrs_now(struct Curl_easy *data) pgrsSetUploadCounter() - amount of data currently uploaded pgrsUpdate() - show progress pgrsDone() - transfer complete - */ int Curl_pgrsDone(struct Curl_easy *data) @@ -245,7 +238,6 @@ void Curl_pgrsSendPause(struct Curl_easy *data, bool enable) } /* - * * Curl_pgrsTimeWas(). Store the timestamp time at the given label. */ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, @@ -292,8 +284,8 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, case TIMER_STARTTRANSFER: delta = &data->progress.t_starttransfer; /* prevent updating t_starttransfer unless: - * 1) this is the first time we are setting t_starttransfer - * 2) a redirect has occurred since the last time t_starttransfer was set + * 1. this is the first time we are setting t_starttransfer + * 2. a redirect has occurred since the last time t_starttransfer was set * This prevents repeated invocations of the function from incorrectly * changing the t_starttransfer time. */ @@ -323,7 +315,6 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, } /* - * * Curl_pgrsTime(). Store the current time at the given label. This fetches a * fresh "now" and returns it. * @@ -421,7 +412,7 @@ static bool progress_calc(struct Curl_easy *data, { struct Progress * const p = &data->progress; int i_next, i_oldest, i_latest; - timediff_t duration_ms; + timediff_t duration_us; curl_off_t amount; /* The time spent so far (from the start) in microseconds */ @@ -472,21 +463,19 @@ static bool progress_calc(struct Curl_easy *data, /* How much we transferred between oldest and current records */ amount = p->speed_amount[i_latest] - p->speed_amount[i_oldest]; /* How long this took */ - duration_ms = curlx_ptimediff_ms(&p->speed_time[i_latest], + duration_us = curlx_ptimediff_us(&p->speed_time[i_latest], &p->speed_time[i_oldest]); - if(duration_ms <= 0) - duration_ms = 1; + if(duration_us <= 0) + duration_us = 1; - if(amount > (CURL_OFF_T_MAX / 1000)) { + if(amount > (CURL_OFF_T_MAX / 1000000)) { /* the 'amount' value is bigger than would fit in 64 bits if - multiplied with 1000, so we use the double math for this */ + multiplied with 1000000, so we use the double math for this */ p->current_speed = - (curl_off_t)(((double)amount * 1000.0) / (double)duration_ms); + (curl_off_t)(((double)amount * 1000000.0) / (double)duration_us); } else { - /* the 'amount' value is small enough to fit within 32 bits even - when multiplied with 1000 */ - p->current_speed = amount * 1000 / duration_ms; + p->current_speed = amount * 1000000 / duration_us; } if((p->lastshow == pnow->tv_sec) && !data->req.done) @@ -609,8 +598,7 @@ static void progress_meter(struct Curl_easy *data) /* we flush the output stream to make it appear as soon as possible */ fflush(data->set.err); } -#else - /* progress bar disabled */ +#else /* CURL_DISABLE_PROGRESS_METER */ #define progress_meter(x) Curl_nop_stmt #endif diff --git a/vendor/curl/lib/rand.c b/vendor/curl/lib/rand.c index a0b168c..4232e81 100644 --- a/vendor/curl/lib/rand.c +++ b/vendor/curl/lib/rand.c @@ -35,9 +35,7 @@ #ifdef _WIN32 -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= _WIN32_WINNT_VISTA && \ - !defined(CURL_WINDOWS_UWP) -# define HAVE_WIN_BCRYPTGENRANDOM +#ifndef CURL_WINDOWS_UWP # include # ifdef _MSC_VER # pragma comment(lib, "bcrypt.lib") @@ -45,38 +43,17 @@ # ifndef STATUS_SUCCESS # define STATUS_SUCCESS ((NTSTATUS)0x00000000L) # endif -#elif defined(USE_WIN32_CRYPTO) -# include -# ifdef _MSC_VER -# pragma comment(lib, "advapi32.lib") -# endif #endif CURLcode Curl_win32_random(unsigned char *entropy, size_t length) { memset(entropy, 0, length); -#ifdef HAVE_WIN_BCRYPTGENRANDOM +#ifndef CURL_WINDOWS_UWP if(BCryptGenRandom(NULL, entropy, (ULONG)length, BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) return CURLE_FAILED_INIT; - return CURLE_OK; -#elif defined(USE_WIN32_CRYPTO) - { - HCRYPTPROV hCryptProv = 0; - - if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return CURLE_FAILED_INIT; - - if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) { - CryptReleaseContext(hCryptProv, 0UL); - return CURLE_FAILED_INIT; - } - - CryptReleaseContext(hCryptProv, 0UL); - } return CURLE_OK; #else return CURLE_NOT_BUILT_IN; @@ -229,12 +206,6 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num) unsigned char buffer[128]; DEBUGASSERT(num > 1); -#ifdef __clang_analyzer__ - /* This silences a scan-build warning about accessing this buffer with - uninitialized memory. */ - memset(buffer, 0, sizeof(buffer)); -#endif - if((num / 2 >= sizeof(buffer)) || !(num & 1)) { /* make sure it fits in the local buffer and that it is an odd number! */ DEBUGF(infof(data, "invalid buffer size with Curl_rand_hex")); diff --git a/vendor/curl/lib/rand.h b/vendor/curl/lib/rand.h index 03c69a0..8676580 100644 --- a/vendor/curl/lib/rand.h +++ b/vendor/curl/lib/rand.h @@ -30,9 +30,9 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, unsigned char *rnd, size_t num); #ifdef DEBUGBUILD -#define Curl_rand(a, b, c) Curl_rand_bytes((a), TRUE, (b), (c)) +#define Curl_rand(a, b, c) Curl_rand_bytes(a, TRUE, b, c) #else -#define Curl_rand(a, b, c) Curl_rand_bytes((a), (b), (c)) +#define Curl_rand(a, b, c) Curl_rand_bytes(a, b, c) #endif /* diff --git a/vendor/curl/lib/ratelimit.c b/vendor/curl/lib/ratelimit.c index c7ba646..2d88cdd 100644 --- a/vendor/curl/lib/ratelimit.c +++ b/vendor/curl/lib/ratelimit.c @@ -23,64 +23,18 @@ ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #include "ratelimit.h" -#define CURL_US_PER_SEC 1000000 -#define CURL_RLIMIT_MIN_CHUNK (16 * 1024) -#define CURL_RLIMIT_MAX_STEPS 2 /* 500ms interval */ +#define CURL_US_PER_SEC 1000000 +#define CURL_RLIMIT_MIN_RATE (4 * 1024) /* minimum step rate */ +#define CURL_RLIMIT_STEP_MIN_MS 2 /* minimum step duration */ -void Curl_rlimit_init(struct Curl_rlimit *r, - curl_off_t rate_per_s, - curl_off_t burst_per_s, - const struct curltime *pts) -{ - curl_off_t rate_steps; - - DEBUGASSERT(rate_per_s >= 0); - DEBUGASSERT(burst_per_s >= rate_per_s || !burst_per_s); - DEBUGASSERT(pts); - r->step_us = CURL_US_PER_SEC; - r->rate_per_step = rate_per_s; - r->burst_per_step = burst_per_s; - /* On rates that are multiples of CURL_RLIMIT_MIN_CHUNK, we reduce - * the interval `step_us` from 1 second to smaller steps with at - * most CURL_RLIMIT_MAX_STEPS. - * Smaller means more CPU, but also more precision. */ - rate_steps = rate_per_s / CURL_RLIMIT_MIN_CHUNK; - rate_steps = CURLMIN(rate_steps, CURL_RLIMIT_MAX_STEPS); - if(rate_steps >= 2) { - r->step_us /= rate_steps; - r->rate_per_step /= rate_steps; - r->burst_per_step /= rate_steps; - } - r->tokens = r->rate_per_step; - r->spare_us = 0; - r->ts = *pts; - r->blocked = FALSE; -} - -void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts) -{ - r->tokens = r->rate_per_step; - r->spare_us = 0; - r->ts = *pts; -} - -bool Curl_rlimit_active(struct Curl_rlimit *r) -{ - return (r->rate_per_step > 0) || r->blocked; -} - -bool Curl_rlimit_is_blocked(struct Curl_rlimit *r) -{ - return r->blocked; -} - -static void ratelimit_update(struct Curl_rlimit *r, - const struct curltime *pts) +static void rlimit_update(struct Curl_rlimit *r, + const struct curltime *pts) { timediff_t elapsed_us, elapsed_steps; - curl_off_t token_gain; + int64_t token_gain; DEBUGASSERT(r->rate_per_step); if((r->ts.tv_sec == pts->tv_sec) && (r->ts.tv_usec == pts->tv_usec)) @@ -102,31 +56,151 @@ static void ratelimit_update(struct Curl_rlimit *r, r->spare_us = elapsed_us % r->step_us; /* How many tokens did we gain since the last update? */ - if(r->rate_per_step > (CURL_OFF_T_MAX / elapsed_steps)) - token_gain = CURL_OFF_T_MAX; + if(r->rate_per_step > (INT64_MAX / elapsed_steps)) + token_gain = INT64_MAX; else { token_gain = r->rate_per_step * elapsed_steps; } - /* Limit the token again by the burst rate per second (if set), so we + if((INT64_MAX - token_gain) > r->tokens) + r->tokens += token_gain; + else + r->tokens = INT64_MAX; + + /* Limit the token again by the burst rate (if set), so we * do not suddenly have a huge number of tokens after inactivity. */ - r->tokens += token_gain; if(r->burst_per_step && (r->tokens > r->burst_per_step)) { r->tokens = r->burst_per_step; } } -curl_off_t Curl_rlimit_avail(struct Curl_rlimit *r, - const struct curltime *pts) +static void rlimit_tune_steps(struct Curl_rlimit *r, + int64_t tokens_total) +{ + int64_t tokens_last, tokens_main, msteps; + + /* Tune the ratelimit at the start *if* we know how many tokens + * are expected to be consumed in total. + * The reason for tuning is that rlimit provides tokens to be consumed + * per "step" which starts out to be a second. The tokens may be consumed + * in full at the beginning of a step. The remainder of the second will + * have no tokens available, effectively blocking the consumption and + * so keeping the "step average" in line. + * This works will up to the last step. When no more tokens are needed, + * no wait will happen and the last step would be too fast. This is + * especially noticeable when only a few steps are needed. + * + * Example: downloading 1.5kb with a ratelimit of 1k could be done in + * roughly 1 second (1k in the first second and the 0.5 at the start of + * the second one). + * + * The tuning tries to make the last step small, using only + * 1 percent of the total tokens (at least 1). The rest of the tokens + * are to be consumed in the steps before by adjusting the duration of + * the step and the amount of tokens it provides. */ + if(!r->rate_per_step || + (tokens_total <= 1) || + (tokens_total > (INT64_MAX / 1000))) + return; + + /* Calculate tokens for the last step and the ones before. */ + tokens_last = tokens_total / 100; + if(!tokens_last) /* less than 100 total, use 1 */ + tokens_last = 1; + else if(tokens_last > CURL_RLIMIT_MIN_RATE) + tokens_last = CURL_RLIMIT_MIN_RATE; + DEBUGASSERT(tokens_last); + tokens_main = tokens_total - tokens_last; + DEBUGASSERT(tokens_main); + + /* how many milli-steps will it take to consume those, give the + * original rate limit per second? */ + DEBUGASSERT(r->step_us == CURL_US_PER_SEC); + + msteps = (tokens_main * 1000 / r->rate_per_step); + if(msteps < CURL_RLIMIT_STEP_MIN_MS) { + /* Steps this small will not work. Do not tune. */ + return; + } + else if(msteps < 1000) { + /* It needs less than one step to provide the needed tokens. + * Make it exactly that long and with exactly those tokens. */ + r->step_us = (timediff_t)msteps * 1000; + r->rate_per_step = tokens_main; + r->tokens = r->rate_per_step; + } + else { + /* More than 1 step. Spread the remainder milli steps and + * the tokens they need to provide across all steps. If integer + * arithmetic can do it. */ + curl_off_t ms_unaccounted = (msteps % 1000); + curl_off_t mstep_inc = (ms_unaccounted / (msteps / 1000)); + if(mstep_inc) { + curl_off_t rate_inc = ((r->rate_per_step * mstep_inc) / 1000); + if(rate_inc) { + r->step_us = CURL_US_PER_SEC + ((timediff_t)mstep_inc * 1000); + r->rate_per_step += rate_inc; + r->tokens = r->rate_per_step; + } + } + } + + if(r->burst_per_step) + r->burst_per_step = r->rate_per_step; +} + +void Curl_rlimit_init(struct Curl_rlimit *r, + int64_t rate_per_sec, + int64_t burst_per_sec, + const struct curltime *pts) +{ + DEBUGASSERT(rate_per_sec >= 0); + DEBUGASSERT(burst_per_sec >= rate_per_sec || !burst_per_sec); + DEBUGASSERT(pts); + r->rate_per_step = rate_per_sec; + r->burst_per_step = burst_per_sec; + r->step_us = CURL_US_PER_SEC; + r->spare_us = 0; + r->tokens = r->rate_per_step; + r->ts = *pts; + r->blocked = FALSE; +} + +void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts, + int64_t total_tokens) +{ + r->tokens = r->rate_per_step; + r->spare_us = 0; + r->ts = *pts; + rlimit_tune_steps(r, total_tokens); +} + +int64_t Curl_rlimit_per_step(struct Curl_rlimit *r) +{ + return r->rate_per_step; +} + +bool Curl_rlimit_active(struct Curl_rlimit *r) +{ + return (r->rate_per_step > 0) || r->blocked; +} + +bool Curl_rlimit_is_blocked(struct Curl_rlimit *r) +{ + return (bool)r->blocked; +} + +int64_t Curl_rlimit_avail(struct Curl_rlimit *r, + const struct curltime *pts) { if(r->blocked) return 0; else if(r->rate_per_step) { - ratelimit_update(r, pts); + rlimit_update(r, pts); return r->tokens; } else - return CURL_OFF_T_MAX; + return INT64_MAX; } void Curl_rlimit_drain(struct Curl_rlimit *r, @@ -136,20 +210,19 @@ void Curl_rlimit_drain(struct Curl_rlimit *r, if(r->blocked || !r->rate_per_step) return; - ratelimit_update(r, pts); -#if SIZEOF_CURL_OFF_T <= SIZEOF_SIZE_T - if(tokens > CURL_OFF_T_MAX) { - r->tokens = CURL_OFF_T_MIN; - return; + rlimit_update(r, pts); +#if 8 <= SIZEOF_SIZE_T + if(tokens > INT64_MAX) { + r->tokens = INT64_MAX; } else #endif { - curl_off_t val = (curl_off_t)tokens; - if((CURL_OFF_T_MIN + val) < r->tokens) + int64_t val = (int64_t)tokens; + if((INT64_MIN + val) < r->tokens) r->tokens -= val; else - r->tokens = CURL_OFF_T_MIN; + r->tokens = INT64_MIN; } } @@ -160,14 +233,18 @@ timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r, if(r->blocked || !r->rate_per_step) return 0; - ratelimit_update(r, pts); + rlimit_update(r, pts); if(r->tokens > 0) return 0; /* How much time will it take tokens to become positive again? * Deduct `spare_us` and check against already elapsed time */ - wait_us = (1 + (-r->tokens / r->rate_per_step)) * r->step_us; - wait_us -= r->spare_us; + wait_us = r->step_us - r->spare_us; + if(r->tokens < 0) { + curl_off_t debt_pct = ((-r->tokens) * 100 / r->rate_per_step); + if(debt_pct) + wait_us += (r->step_us * debt_pct / 100); + } elapsed_us = curlx_ptimediff_us(pts, &r->ts); if(elapsed_us >= wait_us) @@ -176,6 +253,21 @@ timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r, return (wait_us + 999) / 1000; /* in milliseconds */ } +timediff_t Curl_rlimit_next_step_ms(struct Curl_rlimit *r, + const struct curltime *pts) +{ + if(!r->blocked && r->rate_per_step) { + timediff_t elapsed_us, next_us; + + elapsed_us = curlx_ptimediff_us(pts, &r->ts) + r->spare_us; + if(r->step_us > elapsed_us) { + next_us = r->step_us - elapsed_us; + return (next_us + 999) / 1000; /* in milliseconds */ + } + } + return 0; +} + void Curl_rlimit_block(struct Curl_rlimit *r, bool activate, const struct curltime *pts) @@ -188,7 +280,7 @@ void Curl_rlimit_block(struct Curl_rlimit *r, if(!r->blocked) { /* Start rate limiting fresh. The amount of time this was blocked * does not generate extra tokens. */ - Curl_rlimit_start(r, pts); + Curl_rlimit_start(r, pts, -1); } else { r->tokens = 0; diff --git a/vendor/curl/lib/ratelimit.h b/vendor/curl/lib/ratelimit.h index 07f11b8..7563734 100644 --- a/vendor/curl/lib/ratelimit.h +++ b/vendor/curl/lib/ratelimit.h @@ -1,5 +1,5 @@ -#ifndef HEADER_Curl_rlimit_H -#define HEADER_Curl_rlimit_H +#ifndef HEADER_CURL_RLIMIT_H +#define HEADER_CURL_RLIMIT_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -28,8 +28,10 @@ struct Curl_easy; /* This is a rate limiter that provides "tokens" to be consumed - * per second with a "burst" rate limitation. Example: - * A rate limit of 1 megabyte per second with a burst rate of 1.5MB. + * per second. In the literature, this is referred to as a + * "token bucket" (https://en.wikipedia.org/wiki/Token_bucket). + * Example: + * A rate limit of 1 megabyte per second. * - initially 1 million tokens are available. * - these are drained in the first second. * - checking available tokens before the 2nd second will return 0. @@ -43,44 +45,55 @@ struct Curl_easy; * - setting "burst" to the same value as "rate" would make a * download always try to stay *at/below* the rate and slow times will * not generate extra tokens. + * * A rate limit can be blocked, causing the available tokens to become * always 0 until unblocked. After unblocking, the rate limiting starts * again with no history of the past. + * * Finally, a rate limiter with rate 0 will always have CURL_OFF_T_MAX * tokens available, unless blocked. */ struct Curl_rlimit { - curl_off_t rate_per_step; /* rate tokens are generated per step us */ - curl_off_t burst_per_step; /* burst rate of tokens per step us */ + int64_t rate_per_step; /* rate tokens generated per step us */ + int64_t burst_per_step; /* burst rate of tokens per step us */ timediff_t step_us; /* microseconds between token increases */ - curl_off_t tokens; /* tokens available in the next second */ + int64_t tokens; /* tokens available in the next second */ timediff_t spare_us; /* microseconds unaffecting tokens */ struct curltime ts; /* time of the last update */ BIT(blocked); /* blocking sets available tokens to 0 */ }; void Curl_rlimit_init(struct Curl_rlimit *r, - curl_off_t rate_per_s, - curl_off_t burst_per_s, + int64_t rate_per_sec, + int64_t burst_per_sec, const struct curltime *pts); -/* Start ratelimiting with the given timestamp. Resets available tokens. */ -void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts); +/* Start ratelimiting with the given timestamp. Resets available tokens. + * `total_tokens` is either -1 or the number of total tokens expected + * to be consumed. */ +void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts, + int64_t total_tokens); /* How many milliseconds to wait until token are available again. */ timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r, const struct curltime *pts); +/* When the rate limit will update its tokens again */ +timediff_t Curl_rlimit_next_step_ms(struct Curl_rlimit *r, + const struct curltime *pts); + /* Return if rate limiting of tokens is active */ bool Curl_rlimit_active(struct Curl_rlimit *r); bool Curl_rlimit_is_blocked(struct Curl_rlimit *r); +int64_t Curl_rlimit_per_step(struct Curl_rlimit *r); /* Return how many tokens are available to spend, may be negative */ -curl_off_t Curl_rlimit_avail(struct Curl_rlimit *r, - const struct curltime *pts); +int64_t Curl_rlimit_avail(struct Curl_rlimit *r, + const struct curltime *pts); -/* Drain tokens from the ratelimit, return how many are now available. */ +/* Drain tokens from the ratelimit, give an estimate of how many tokens + * remain to be drained in the future (-1 for unknown). */ void Curl_rlimit_drain(struct Curl_rlimit *r, size_t tokens, const struct curltime *pts); @@ -90,4 +103,4 @@ void Curl_rlimit_block(struct Curl_rlimit *r, bool activate, const struct curltime *pts); -#endif /* HEADER_Curl_rlimit_H */ +#endif /* HEADER_CURL_RLIMIT_H */ diff --git a/vendor/curl/lib/request.c b/vendor/curl/lib/request.c index ad7f628..6607753 100644 --- a/vendor/curl/lib/request.c +++ b/vendor/curl/lib/request.c @@ -158,6 +158,9 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) req->no_body = data->set.opt_no_body; req->authneg = FALSE; req->shutdown = FALSE; + /* Unpause all directions */ + Curl_rlimit_block(&data->progress.dl.rlimit, FALSE, &t0); + Curl_rlimit_block(&data->progress.ul.rlimit, FALSE, &t0); } void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) @@ -292,7 +295,7 @@ static CURLcode req_flush(struct Curl_easy *data) return result; if(!Curl_bufq_is_empty(&data->req.sendbuf)) { DEBUGF(infof(data, "Curl_req_flush(len=%zu) -> EAGAIN", - Curl_bufq_len(&data->req.sendbuf))); + Curl_bufq_len(&data->req.sendbuf))); return CURLE_AGAIN; } } diff --git a/vendor/curl/lib/request.h b/vendor/curl/lib/request.h index 389ae4d..5332d48 100644 --- a/vendor/curl/lib/request.h +++ b/vendor/curl/lib/request.h @@ -32,7 +32,7 @@ struct UserDefined; enum expect100 { - EXP100_SEND_DATA, /* enough waiting, just send the body now */ + EXP100_SEND_DATA, /* enough waiting, send the body now */ EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */ EXP100_SENDING_REQUEST, /* still sending the request but will wait for the 100 header once done with the request */ @@ -174,11 +174,11 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data); * they will be buffered. Use `Curl_req_flush()` to make sure * bytes are really send. * @param data the transfer making the request - * @param buf the complete header bytes, no body + * @param req the complete header bytes, no body * @param httpversion version used in request (09, 10, 11, etc.) * @return CURLE_OK (on blocking with *pnwritten == 0) or error. */ -CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf, +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req, unsigned char httpversion); /** diff --git a/vendor/curl/lib/rtsp.c b/vendor/curl/lib/rtsp.c index 93bcbeb..11848c8 100644 --- a/vendor/curl/lib/rtsp.c +++ b/vendor/curl/lib/rtsp.c @@ -22,10 +22,11 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "rtsp.h" #ifndef CURL_DISABLE_RTSP -#include "urldata.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" @@ -33,12 +34,11 @@ #include "http.h" #include "url.h" #include "progress.h" -#include "rtsp.h" #include "strcase.h" #include "select.h" #include "connect.h" #include "cfilters.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "bufref.h" #include "curlx/strparse.h" @@ -66,45 +66,13 @@ struct rtsp_conn { /* RTSP transfer data */ struct RTSP { - long CSeq_sent; /* CSeq of this request */ - long CSeq_recv; /* CSeq received */ + uint32_t CSeq_sent; /* CSeq of this request */ + uint32_t CSeq_recv; /* CSeq received */ }; #define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \ ((unsigned int)((unsigned char)((p)[3])))) -/* protocol-specific functions set up to be called by the main engine */ -static CURLcode rtsp_do(struct Curl_easy *data, bool *done); -static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature); -static CURLcode rtsp_connect(struct Curl_easy *data, bool *done); -static CURLcode rtsp_do_pollset(struct Curl_easy *data, - struct easy_pollset *ps); - -/* - * Parse and write out an RTSP response. - * @param data the transfer - * @param conn the connection - * @param buf data read from connection - * @param blen amount of data in buf - * @param is_eos TRUE iff this is the last write - * @param readmore out, TRUE iff complete buf was consumed and more data - * is needed - */ -static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, - const char *buf, - size_t blen, - bool is_eos); -static CURLcode rtsp_rtp_write_resp_hd(struct Curl_easy *data, - const char *buf, - size_t blen, - bool is_eos); - -static CURLcode rtsp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static unsigned int rtsp_conncheck(struct Curl_easy *data, - struct connectdata *check, - unsigned int checks_to_perform); - /* this returns the socket to wait for in the DO and DOING state for the multi interface and then we are always _sending_ a request and thus we wait for the single socket to become writable only */ @@ -115,39 +83,6 @@ static CURLcode rtsp_do_pollset(struct Curl_easy *data, return Curl_pollset_add_out(data, ps, data->conn->sock[FIRSTSOCKET]); } -static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, - size_t len); -static CURLcode rtsp_parse_transport(struct Curl_easy *data, - const char *transport); - -/* - * RTSP handler interface. - */ -const struct Curl_handler Curl_handler_rtsp = { - "rtsp", /* scheme */ - rtsp_setup_connection, /* setup_connection */ - rtsp_do, /* do_it */ - rtsp_done, /* done */ - ZERO_NULL, /* do_more */ - rtsp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - rtsp_do_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - Curl_http_perform_pollset, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - rtsp_rtp_write_resp, /* write_resp */ - rtsp_rtp_write_resp_hd, /* write_resp_hd */ - rtsp_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - Curl_http_follow, /* follow */ - PORT_RTSP, /* defport */ - CURLPROTO_RTSP, /* protocol */ - CURLPROTO_RTSP, /* family */ - PROTOPT_CONN_REUSE /* flags */ -}; - #define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */ static void rtsp_easy_dtor(void *key, size_t klen, void *entry) @@ -191,12 +126,11 @@ static CURLcode rtsp_setup_connection(struct Curl_easy *data, /* * Function to check on various aspects of a connection. */ -static unsigned int rtsp_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) +static uint32_t rtsp_conncheck(struct Curl_easy *data, + struct connectdata *conn, + uint32_t checks_to_perform) { unsigned int ret_val = CONNRESULT_NONE; - (void)data; if(checks_to_perform & CONNCHECK_ISDEAD) { bool input_pending; @@ -245,16 +179,16 @@ static CURLcode rtsp_done(struct Curl_easy *data, if(!status && !httpStatus) { /* Check the sequence numbers */ - long CSeq_sent = rtsp->CSeq_sent; - long CSeq_recv = rtsp->CSeq_recv; + uint32_t CSeq_sent = rtsp->CSeq_sent; + uint32_t CSeq_recv = rtsp->CSeq_recv; if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) { failf(data, - "The CSeq of this request %ld did not match the response %ld", + "The CSeq of this request %u did not match the response %u", CSeq_sent, CSeq_recv); return CURLE_RTSP_CSEQ_ERROR; } if(data->set.rtspreq == RTSPREQ_RECEIVE && (rtspc->rtp_channel == -1)) { - infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv); + infof(data, "Got an RTP Receive with a CSeq of %u", CSeq_recv); } if(data->set.rtspreq == RTSPREQ_RECEIVE && data->req.eos_written) { @@ -346,11 +280,11 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - Curl_RtspReq rtspreq = data->set.rtspreq; + const Curl_RtspReq rtspreq = data->set.rtspreq; struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); struct dynbuf req_buffer; - unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort of... */ - + const unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort + of... */ const char *p_request = NULL; const char *p_session_id = NULL; const char *p_accept = NULL; @@ -382,7 +316,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->handler->protocol; + data->state.first_remote_protocol = conn->scheme->protocol; } /* Setup the 'p_request' pointer to the proper p_request string @@ -573,7 +507,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) result = curlx_dyn_addf(&req_buffer, "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ - "CSeq: %ld\r\n", /* CSeq */ + "CSeq: %u\r\n", /* CSeq */ p_request, p_stream_uri, rtsp->CSeq_sent); if(result) goto out; @@ -685,6 +619,48 @@ static CURLcode rtp_write_body_junk(struct Curl_easy *data, return CURLE_OK; } +static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, + size_t len) +{ + size_t wrote; + curl_write_callback writeit; + void *user_ptr; + + if(len == 0) { + failf(data, "Cannot write a 0 size RTP packet."); + return CURLE_WRITE_ERROR; + } + + /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that + function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP + data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA + pointer to write out the RTP data. */ + if(data->set.fwrite_rtp) { + writeit = data->set.fwrite_rtp; + user_ptr = data->set.rtp_out; + } + else { + writeit = data->set.fwrite_func; + user_ptr = data->set.out; + } + + Curl_set_in_callback(data, TRUE); + wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr); + Curl_set_in_callback(data, FALSE); + + if(wrote == CURL_WRITEFUNC_PAUSE) { + failf(data, "Cannot pause RTP"); + return CURLE_WRITE_ERROR; + } + + if(wrote != len) { + failf(data, "Failed writing RTP data"); + return CURLE_WRITE_ERROR; + } + + return CURLE_OK; +} + static CURLcode rtsp_filter_rtp(struct Curl_easy *data, struct rtsp_conn *rtspc, const char *buf, @@ -798,6 +774,18 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data, break; rtp_buf = curlx_dyn_ptr(&rtspc->buf); rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4; + if(rtspc->rtp_len == 4) { + /* zero-length payload, the 4-byte header is the complete RTP + message. Dispatch immediately without entering RTP_PARSE_DATA. */ + DEBUGF(infof(data, "RTP write channel %d rtp_len %zu (no payload)", + rtspc->rtp_channel, rtspc->rtp_len)); + result = rtp_client_write(data, rtp_buf, rtspc->rtp_len); + curlx_dyn_free(&rtspc->buf); + rtspc->state = RTP_PARSE_SKIP; + if(result) + goto out; + break; + } rtspc->state = RTP_PARSE_DATA; break; } @@ -848,6 +836,16 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data, return result; } +/* + * Parse and write out an RTSP response. + * @param data the transfer + * @param conn the connection + * @param buf data read from connection + * @param blen amount of data in buf + * @param is_eos TRUE iff this is the last write + * @param readmore out, TRUE iff complete buf was consumed and more data + * is needed + */ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, const char *buf, size_t blen, @@ -916,8 +914,8 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, /* we SHOULD have consumed all bytes, unless the response is borked. * In which case we write out the left over bytes, letting the client * writer deal with it (it will report EXCESS and fail the transfer). */ - DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d " - " rtspc->state=%d, req.size=%" FMT_OFF_T ")", + DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d, " + "rtspc->state=%d, req.size=%" FMT_OFF_T ")", blen, rtspc->in_header, data->req.done, rtspc->state, data->req.size)); if(!result && (is_eos || blen)) { @@ -928,7 +926,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, out: if((data->set.rtspreq == RTSPREQ_RECEIVE) && (rtspc->state == RTP_PARSE_SKIP)) { - /* In special mode RECEIVE, we just process one chunk of network + /* In special mode RECEIVE, we process one chunk of network * data, so we stop the transfer here, if we have no incomplete * RTP message pending. */ data->req.download_done = TRUE; @@ -944,45 +942,46 @@ static CURLcode rtsp_rtp_write_resp_hd(struct Curl_easy *data, return rtsp_rtp_write_resp(data, buf, blen, is_eos); } -static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, - size_t len) +static CURLcode rtsp_parse_transport(struct Curl_easy *data, + const char *transport) { - size_t wrote; - curl_write_callback writeit; - void *user_ptr; - - if(len == 0) { - failf(data, "Cannot write a 0 size RTP packet."); - return CURLE_WRITE_ERROR; - } - - /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that - function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP - data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA - pointer to write out the RTP data. */ - if(data->set.fwrite_rtp) { - writeit = data->set.fwrite_rtp; - user_ptr = data->set.rtp_out; - } - else { - writeit = data->set.fwrite_func; - user_ptr = data->set.out; - } - - Curl_set_in_callback(data, TRUE); - wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr); - Curl_set_in_callback(data, FALSE); - - if(CURL_WRITEFUNC_PAUSE == wrote) { - failf(data, "Cannot pause RTP"); - return CURLE_WRITE_ERROR; - } - - if(wrote != len) { - failf(data, "Failed writing RTP data"); - return CURLE_WRITE_ERROR; + /* If we receive multiple Transport response-headers, the interleaved + channels of each response header is recorded and used together for + subsequent data validity checks.*/ + /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */ + const char *start, *end; + start = transport; + while(start && *start) { + curlx_str_passblanks(&start); + end = strchr(start, ';'); + if(checkprefix("interleaved=", start)) { + curl_off_t chan1, chan2, chan; + const char *p = start + 12; + if(!curlx_str_number(&p, &chan1, 255)) { + unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; + chan2 = chan1; + if(!curlx_str_single(&p, '-')) { + if(curlx_str_number(&p, &chan2, 255)) { + infof(data, "Unable to read the interleaved parameter from " + "Transport header: [%s]", transport); + chan2 = chan1; + } + } + for(chan = chan1; chan <= chan2; chan++) { + int idx = (int)chan / 8; + int off = (int)chan % 8; + rtp_channel_mask[idx] |= (unsigned char)(1 << off); + } + } + else { + infof(data, "Unable to read the interleaved parameter from " + "Transport header: [%s]", transport); + } + break; + } + /* skip to next parameter */ + start = (!end) ? end : (end + 1); } - return CURLE_OK; } @@ -995,12 +994,11 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) if(!rtsp) return CURLE_FAILED_INIT; curlx_str_passblanks(&p); - if(curlx_str_number(&p, &CSeq, LONG_MAX)) { + if(curlx_str_number(&p, &CSeq, UINT_MAX)) { failf(data, "Unable to read the CSeq header: [%s]", header); return CURLE_RTSP_CSEQ_ERROR; } - rtsp->CSeq_recv = (long)CSeq; /* mark the request */ - data->state.rtsp_CSeq_recv = (long)CSeq; /* update the handle */ + data->state.rtsp_CSeq_recv = rtsp->CSeq_recv = (uint32_t)CSeq; } else if(checkprefix("Session:", header)) { const char *start, *end; @@ -1042,7 +1040,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) */ /* Copy the id substring into a new buffer */ - data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen); + data->set.str[STRING_RTSP_SESSION_ID] = curlx_memdup0(start, idlen); if(!data->set.str[STRING_RTSP_SESSION_ID]) return CURLE_OUT_OF_MEMORY; } @@ -1056,47 +1054,43 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) return CURLE_OK; } -static CURLcode rtsp_parse_transport(struct Curl_easy *data, - const char *transport) -{ - /* If we receive multiple Transport response-headers, the interleaved - channels of each response header is recorded and used together for - subsequent data validity checks.*/ - /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */ - const char *start, *end; - start = transport; - while(start && *start) { - curlx_str_passblanks(&start); - end = strchr(start, ';'); - if(checkprefix("interleaved=", start)) { - curl_off_t chan1, chan2, chan; - const char *p = start + 12; - if(!curlx_str_number(&p, &chan1, 255)) { - unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; - chan2 = chan1; - if(!curlx_str_single(&p, '-')) { - if(curlx_str_number(&p, &chan2, 255)) { - infof(data, "Unable to read the interleaved parameter from " - "Transport header: [%s]", transport); - chan2 = chan1; - } - } - for(chan = chan1; chan <= chan2; chan++) { - int idx = (int)chan / 8; - int off = (int)chan % 8; - rtp_channel_mask[idx] |= (unsigned char)(1 << off); - } - } - else { - infof(data, "Unable to read the interleaved parameter from " - "Transport header: [%s]", transport); - } - break; - } - /* skip to next parameter */ - start = (!end) ? end : (end + 1); - } - return CURLE_OK; -} +/* + * RTSP handler interface. + */ +static const struct Curl_protocol Curl_protocol_rtsp = { + rtsp_setup_connection, /* setup_connection */ + rtsp_do, /* do_it */ + rtsp_done, /* done */ + ZERO_NULL, /* do_more */ + rtsp_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + rtsp_do_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + Curl_http_perform_pollset, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + rtsp_rtp_write_resp, /* write_resp */ + rtsp_rtp_write_resp_hd, /* write_resp_hd */ + rtsp_conncheck, /* connection_check */ + ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ +}; #endif /* CURL_DISABLE_RTSP */ + +/* + * RTSP handler interface. + */ +const struct Curl_scheme Curl_scheme_rtsp = { + "rtsp", /* scheme */ +#ifdef CURL_DISABLE_RTSP + ZERO_NULL, +#else + &Curl_protocol_rtsp, +#endif + CURLPROTO_RTSP, /* protocol */ + CURLPROTO_RTSP, /* family */ + PROTOPT_CONN_REUSE, /* flags */ + PORT_RTSP, /* defport */ +}; diff --git a/vendor/curl/lib/rtsp.h b/vendor/curl/lib/rtsp.h index 70ca00f..92b28e5 100644 --- a/vendor/curl/lib/rtsp.h +++ b/vendor/curl/lib/rtsp.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +extern const struct Curl_scheme Curl_scheme_rtsp; #ifndef CURL_DISABLE_RTSP -extern const struct Curl_handler Curl_handler_rtsp; CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header); #else #define Curl_rtsp_parseheader(x, y) CURLE_NOT_BUILT_IN diff --git a/vendor/curl/lib/select.c b/vendor/curl/lib/select.c index cae5085..9a11924 100644 --- a/vendor/curl/lib/select.c +++ b/vendor/curl/lib/select.c @@ -66,7 +66,7 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */ if((!fds_read || fds_read->fd_count == 0) && (!fds_write || fds_write->fd_count == 0) && (!fds_err || fds_err->fd_count == 0)) { - /* no sockets, just wait */ + /* no sockets, wait */ return curlx_wait_ms(timeout_ms); } #endif @@ -82,9 +82,9 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */ given as null. At least one must be non-null, and any non-null descriptor set must contain at least one handle to a socket. - It is unclear why Winsock does not just handle this for us instead of + It is unclear why Winsock does not handle this for us instead of calling this an error. Luckily, with Winsock, we can _also_ ask how - many bits are set on an fd_set. So, let's just check it beforehand. + many bits are set on an fd_set. So, let's check it beforehand. */ return select((int)maxfd + 1, fds_read && fds_read->fd_count ? fds_read : NULL, @@ -128,7 +128,7 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) { - /* no sockets, just wait */ + /* no sockets, wait */ return curlx_wait_ms(timeout_ms); } @@ -223,7 +223,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) } } if(fds_none) { - /* no sockets, just wait */ + /* no sockets, wait */ return curlx_wait_ms(timeout_ms); } @@ -568,7 +568,6 @@ CURLcode Curl_pollset_change(struct Curl_easy *data, DEBUGASSERT(ps->init == CURL_EASY_POLLSET_MAGIC); #endif - (void)data; DEBUGASSERT(VALID_SOCK(sock)); if(!VALID_SOCK(sock)) return CURLE_BAD_FUNCTION_ARGUMENT; diff --git a/vendor/curl/lib/select.h b/vendor/curl/lib/select.h index 268ae7c..ffcd44c 100644 --- a/vendor/curl/lib/select.h +++ b/vendor/curl/lib/select.h @@ -71,7 +71,8 @@ struct pollfd { therefore defined here */ #define CURL_CSELECT_IN2 (CURL_CSELECT_ERR << 1) -int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2, +int Curl_socket_check(curl_socket_t readfd0, + curl_socket_t readfd1, curl_socket_t writefd, timediff_t timeout_ms); #define SOCKET_READABLE(x, z) \ @@ -160,22 +161,19 @@ CURLcode Curl_pollset_set(struct Curl_easy *data, bool do_in, bool do_out) WARN_UNUSED_RESULT; #define Curl_pollset_add_in(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0) + Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0) #define Curl_pollset_remove_in(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), 0, CURL_POLL_IN) + Curl_pollset_change(data, ps, sock, 0, CURL_POLL_IN) #define Curl_pollset_add_out(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0) + Curl_pollset_change(data, ps, sock, CURL_POLL_OUT, 0) #define Curl_pollset_remove_out(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), 0, CURL_POLL_OUT) + Curl_pollset_change(data, ps, sock, 0, CURL_POLL_OUT) #define Curl_pollset_add_inout(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_IN | CURL_POLL_OUT, 0) + Curl_pollset_change(data, ps, sock, CURL_POLL_IN | CURL_POLL_OUT, 0) #define Curl_pollset_set_in_only(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_IN, CURL_POLL_OUT) + Curl_pollset_change(data, ps, sock, CURL_POLL_IN, CURL_POLL_OUT) #define Curl_pollset_set_out_only(data, ps, sock) \ - Curl_pollset_change((data), (ps), (sock), \ - CURL_POLL_OUT, CURL_POLL_IN) + Curl_pollset_change(data, ps, sock, CURL_POLL_OUT, CURL_POLL_IN) /* return < = on error, 0 on timeout or how many sockets are ready */ int Curl_pollset_poll(struct Curl_easy *data, diff --git a/vendor/curl/lib/sendf.c b/vendor/curl/lib/sendf.c index 2d66ed5..92e77b4 100644 --- a/vendor/curl/lib/sendf.c +++ b/vendor/curl/lib/sendf.c @@ -42,45 +42,8 @@ #include "cw-out.h" #include "cw-pause.h" #include "multiif.h" -#include "strerror.h" #include "progress.h" - -static CURLcode do_init_writer_stack(struct Curl_easy *data); - -/* Curl_client_write() sends data to the write callback(s) - - The bit pattern defines to what "streams" to write to. Body and/or header. - The defines are in sendf.h of course. - */ -CURLcode Curl_client_write(struct Curl_easy *data, - int type, const char *buf, size_t blen) -{ - CURLcode result; - - /* it is one of those, at least */ - DEBUGASSERT(type & - (CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO)); - /* BODY is only BODY (with optional EOS) */ - DEBUGASSERT(!(type & CLIENTWRITE_BODY) || - ((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0)); - /* INFO is only INFO (with optional EOS) */ - DEBUGASSERT(!(type & CLIENTWRITE_INFO) || - ((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0)); - - if(!data->req.writer_stack) { - result = do_init_writer_stack(data); - if(result) - return result; - DEBUGASSERT(data->req.writer_stack); - } - - result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen); - CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", - type, blen, result); - return result; -} - static void cl_reset_writer(struct Curl_easy *data) { struct Curl_cwriter *writer = data->req.writer_stack; @@ -153,7 +116,7 @@ CURLcode Curl_client_start(struct Curl_easy *data) bool Curl_creader_will_rewind(struct Curl_easy *data) { - return data->req.rewind_read; + return (bool)data->req.rewind_read; } void Curl_creader_set_rewind(struct Curl_easy *data, bool enable) @@ -205,7 +168,9 @@ static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit) struct cw_download_ctx { struct Curl_cwriter super; BIT(started_response); + BIT(started_body); }; + /* Download client writer in phase CURL_CW_PROTOCOL that * sees the "real" download body data. */ static CURLcode cw_download_write(struct Curl_easy *data, @@ -220,7 +185,6 @@ static CURLcode cw_download_write(struct Curl_easy *data, if(!ctx->started_response && !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) { Curl_pgrsTime(data, TIMER_STARTTRANSFER); - Curl_rlimit_start(&data->progress.dl.rlimit, Curl_pgrs_now(data)); ctx->started_response = TRUE; } @@ -233,6 +197,13 @@ static CURLcode cw_download_write(struct Curl_easy *data, return result; } + if(!ctx->started_body && + !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) { + Curl_rlimit_start(&data->progress.dl.rlimit, Curl_pgrs_now(data), + data->req.size); + ctx->started_body = TRUE; + } + /* Here, we deal with REAL BODY bytes. All filtering and transfer * encodings have been applied and only the true content, e.g. BODY, * bytes are passed here. @@ -351,6 +322,84 @@ static const struct Curl_cwtype cw_raw = { sizeof(struct Curl_cwriter) }; +static CURLcode do_init_writer_stack(struct Curl_easy *data) +{ + struct Curl_cwriter *writer; + CURLcode result; + + DEBUGASSERT(!data->req.writer_stack); + result = Curl_cwriter_create(&data->req.writer_stack, + data, &Curl_cwt_out, CURL_CW_CLIENT); + if(result) + return result; + + /* This places the "pause" writer behind the "download" writer that + * is added below. Meaning the "download" can do checks on content length + * and other things *before* write outs are buffered for paused transfers. */ + result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause, + CURL_CW_PROTOCOL); + if(!result) { + result = Curl_cwriter_add(data, writer); + if(result) + Curl_cwriter_free(data, writer); + } + if(result) + return result; + + result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); + if(!result) { + result = Curl_cwriter_add(data, writer); + if(result) + Curl_cwriter_free(data, writer); + } + if(result) + return result; + + result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); + if(!result) { + result = Curl_cwriter_add(data, writer); + if(result) + Curl_cwriter_free(data, writer); + } + if(result) + return result; + + return result; +} + +/* Curl_client_write() sends data to the write callback(s) + + The bit pattern defines to what "streams" to write to. Body and/or header. + The defines are in sendf.h of course. + */ +CURLcode Curl_client_write(struct Curl_easy *data, + int type, const char *buf, size_t len) +{ + CURLcode result; + + /* it is one of those, at least */ + DEBUGASSERT(type & + (CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO)); + /* BODY is only BODY (with optional EOS) */ + DEBUGASSERT(!(type & CLIENTWRITE_BODY) || + ((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0)); + /* INFO is only INFO (with optional EOS) */ + DEBUGASSERT(!(type & CLIENTWRITE_INFO) || + ((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0)); + + if(!data->req.writer_stack) { + result = do_init_writer_stack(data); + if(result) + return result; + DEBUGASSERT(data->req.writer_stack); + } + + result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, len); + CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", + type, len, result); + return result; +} + /* Create an unencoding writer stage using the given handler. */ CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, struct Curl_easy *data, @@ -400,51 +449,6 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase) return n; } -static CURLcode do_init_writer_stack(struct Curl_easy *data) -{ - struct Curl_cwriter *writer; - CURLcode result; - - DEBUGASSERT(!data->req.writer_stack); - result = Curl_cwriter_create(&data->req.writer_stack, - data, &Curl_cwt_out, CURL_CW_CLIENT); - if(result) - return result; - - /* This places the "pause" writer behind the "download" writer that - * is added below. Meaning the "download" can do checks on content length - * and other things *before* write outs are buffered for paused transfers. */ - result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause, - CURL_CW_PROTOCOL); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } - if(result) - return result; - - result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } - if(result) - return result; - - result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } - if(result) - return result; - - return result; -} - CURLcode Curl_cwriter_add(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -625,7 +629,6 @@ struct cr_in_ctx { static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader) { struct cr_in_ctx *ctx = reader->ctx; - (void)data; ctx->read_cb = data->state.fread_func; ctx->cb_user_data = data->state.in; ctx->total_len = -1; @@ -692,9 +695,9 @@ static CURLcode cr_in_read(struct Curl_easy *data, break; case CURL_READFUNC_PAUSE: - if(data->conn->handler->flags & PROTOPT_NONETWORK) { + if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it cannot pause since the transfer + actually only FILE:// now, and it cannot pause since the transfer is not done using the "normal" procedure. */ failf(data, "Read callback asked for PAUSE when not supported"); result = CURLE_READ_ERROR; @@ -723,7 +726,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, if(ctx->total_len >= 0) ctx->seen_eos = (ctx->read_len >= ctx->total_len); *pnread = nread; - *peos = ctx->seen_eos; + *peos = (bool)ctx->seen_eos; break; } CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"FMT_OFF_T @@ -738,7 +741,7 @@ static bool cr_in_needs_rewind(struct Curl_easy *data, { struct cr_in_ctx *ctx = reader->ctx; (void)data; - return ctx->has_used_cb; + return (bool)ctx->has_used_cb; } static curl_off_t cr_in_total_length(struct Curl_easy *data, @@ -899,7 +902,7 @@ static bool cr_in_is_paused(struct Curl_easy *data, { struct cr_in_ctx *ctx = reader->ctx; (void)data; - return ctx->is_paused; + return (bool)ctx->is_paused; } static const struct Curl_crtype cr_in = { @@ -1009,7 +1012,7 @@ static CURLcode cr_lc_read(struct Curl_easy *data, if(ctx->read_eos) ctx->eos = TRUE; *pnread = nread; - *peos = ctx->eos; + *peos = (bool)ctx->eos; goto out; } @@ -1192,7 +1195,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, DEBUGASSERT(data->req.reader_stack); } if(!data->req.reader_started) { - Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data)); + Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data), -1); data->req.reader_started = TRUE; } @@ -1294,7 +1297,6 @@ static CURLcode cr_buf_read(struct Curl_easy *data, struct cr_buf_ctx *ctx = reader->ctx; size_t nread = ctx->blen - ctx->index; - (void)data; if(!nread || !ctx->buf) { *pnread = 0; *peos = TRUE; diff --git a/vendor/curl/lib/sendf.h b/vendor/curl/lib/sendf.h index 1521ccb..75c6e24 100644 --- a/vendor/curl/lib/sendf.h +++ b/vendor/curl/lib/sendf.h @@ -55,10 +55,10 @@ struct Curl_cwriter; struct Curl_easy; /** - * Write `len` bytes at `prt` to the client. `type` indicates what + * Write `len` bytes at `buf` to the client. `type` indicates what * kind of data is being written. */ -CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *ptr, +CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, size_t len) WARN_UNUSED_RESULT; /** @@ -140,7 +140,7 @@ struct Curl_cwriter { */ CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, struct Curl_easy *data, - const struct Curl_cwtype *ce_handler, + const struct Curl_cwtype *cwt, Curl_cwriter_phase phase); /** @@ -298,7 +298,7 @@ void Curl_creader_clear_eos(struct Curl_easy *data, */ CURLcode Curl_creader_create(struct Curl_creader **preader, struct Curl_easy *data, - const struct Curl_crtype *cr_handler, + const struct Curl_crtype *crt, Curl_creader_phase phase); /** diff --git a/vendor/curl/lib/setopt.c b/vendor/curl/lib/setopt.c index 0c1da5b..84f3e02 100644 --- a/vendor/curl/lib/setopt.c +++ b/vendor/curl/lib/setopt.c @@ -46,7 +46,7 @@ #include "altsvc.h" #include "hsts.h" #include "tftp.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "escape.h" #include "bufref.h" @@ -107,7 +107,7 @@ CURLcode Curl_setblobopt(struct curl_blob **blobp, if(blob) { struct curl_blob *nblob; - if(blob->len > CURL_MAX_INPUT_LENGTH) + if(!blob->len || (blob->len > CURL_MAX_INPUT_LENGTH)) return CURLE_BAD_FUNCTION_ARGUMENT; nblob = (struct curl_blob *) curlx_malloc(sizeof(struct curl_blob) + @@ -128,7 +128,8 @@ CURLcode Curl_setblobopt(struct curl_blob **blobp, return CURLE_OK; } -static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) +static CURLcode setstropt_userpwd(const char *option, char **userp, + char **passwdp) { char *user = NULL; char *passwd = NULL; @@ -188,8 +189,10 @@ static CURLcode setstropt_interface(char *option, char **devp, return CURLE_OK; } -#define C_SSLVERSION_VALUE(x) (x & 0xffff) -#define C_SSLVERSION_MAX_VALUE(x) ((unsigned long)x & 0xffff0000) +#ifdef USE_SSL +#define C_SSLVERSION_VALUE(x) ((x) & 0xffff) +#define C_SSLVERSION_MAX_VALUE(x) ((unsigned long)(x) & 0xffff0000) +#endif static CURLcode protocol2num(const char *str, curl_prot_t *val) { @@ -214,9 +217,9 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) str = strchr(str, ','); tlen = str ? (size_t)(str - token) : strlen(token); if(tlen) { - const struct Curl_handler *h = Curl_getn_scheme_handler(token, tlen); + const struct Curl_scheme *h = Curl_getn_scheme(token, tlen); - if(!h) + if(!h || !h->run) return CURLE_UNSUPPORTED_PROTOCOL; *val |= h->protocol; @@ -269,9 +272,9 @@ static CURLcode httpauth(struct Curl_easy *data, bool proxy, return CURLE_NOT_BUILT_IN; /* no supported types left! */ } if(proxy) - data->set.proxyauth = auth; + data->set.proxyauth = (uint32_t)auth; else - data->set.httpauth = auth; + data->set.httpauth = (uint32_t)auth; return CURLE_OK; } #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ @@ -356,51 +359,41 @@ CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option, static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg) { /* - * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) - * Would this be better if the RTSPREQ_* were just moved into here? + * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) Would this be + * better if the RTSPREQ_* were moved into here? */ Curl_RtspReq rtspreq = RTSPREQ_NONE; switch(arg) { case CURL_RTSPREQ_OPTIONS: rtspreq = RTSPREQ_OPTIONS; break; - case CURL_RTSPREQ_DESCRIBE: rtspreq = RTSPREQ_DESCRIBE; break; - case CURL_RTSPREQ_ANNOUNCE: rtspreq = RTSPREQ_ANNOUNCE; break; - case CURL_RTSPREQ_SETUP: rtspreq = RTSPREQ_SETUP; break; - case CURL_RTSPREQ_PLAY: rtspreq = RTSPREQ_PLAY; break; - case CURL_RTSPREQ_PAUSE: rtspreq = RTSPREQ_PAUSE; break; - case CURL_RTSPREQ_TEARDOWN: rtspreq = RTSPREQ_TEARDOWN; break; - case CURL_RTSPREQ_GET_PARAMETER: rtspreq = RTSPREQ_GET_PARAMETER; break; - case CURL_RTSPREQ_SET_PARAMETER: rtspreq = RTSPREQ_SET_PARAMETER; break; - case CURL_RTSPREQ_RECORD: rtspreq = RTSPREQ_RECORD; break; - case CURL_RTSPREQ_RECEIVE: rtspreq = RTSPREQ_RECEIVE; break; @@ -429,8 +422,8 @@ static void set_ssl_options(struct ssl_config_data *ssl, } #endif -static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, - long arg, bool *set) +static CURLcode setopt_long_bool(struct Curl_easy *data, CURLoption option, + long arg) { bool enabled = !!arg; int ok = 1; @@ -536,7 +529,6 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, */ s->http_auto_referer = enabled; break; - case CURLOPT_TRANSFER_ENCODING: s->http_transfer_encoding = enabled; break; @@ -547,21 +539,18 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, */ s->allow_auth_to_other_hosts = enabled; break; - case CURLOPT_HTTP_TRANSFER_DECODING: /* * disable libcurl transfer encoding is used */ s->http_te_skip = !enabled; /* reversed */ break; - case CURLOPT_HTTP_CONTENT_DECODING: /* * raw data passed to the application when content encoding is used */ s->http_ce_skip = !enabled; /* reversed */ break; - case CURLOPT_HTTPGET: /* * Set to force us do HTTP GET @@ -649,11 +638,9 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, case CURLOPT_FTP_USE_EPRT: s->ftp_use_eprt = enabled; break; - case CURLOPT_FTP_USE_EPSV: s->ftp_use_epsv = enabled; break; - case CURLOPT_FTP_USE_PRET: s->ftp_use_pret = enabled; break; @@ -674,7 +661,6 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, */ s->crlf = enabled; break; - #ifndef CURL_DISABLE_TFTP case CURLOPT_TFTP_NO_OPTIONS: /* @@ -775,7 +761,6 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, */ s->tcp_nodelay = enabled; break; - case CURLOPT_IGNORE_CONTENT_LENGTH: s->ignorecl = enabled; break; @@ -835,14 +820,13 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, s->quick_exit = enabled; break; default: - return CURLE_OK; + return CURLE_UNKNOWN_OPTION; } if((arg > ok) || (arg < 0)) /* reserve other values for future use */ infof(data, "boolean setopt(%d) got unsupported argument %ld," " treated as %d", option, arg, enabled); - *set = TRUE; return CURLE_OK; } @@ -857,20 +841,11 @@ static CURLcode value_range(long *value, long below_error, long min, long max) return CURLE_OK; } -static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, - long arg) +static CURLcode setopt_long_net(struct Curl_easy *data, CURLoption option, + long arg) { -#if !defined(CURL_DISABLE_PROXY) || \ - !defined(CURL_DISABLE_HTTP) || \ - defined(HAVE_GSSAPI) || \ - defined(USE_IPV6) - unsigned long uarg = (unsigned long)arg; -#endif - bool set = FALSE; - CURLcode result = setopt_bool(data, option, arg, &set); + CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; - if(set || result) - return result; switch(option) { case CURLOPT_DNS_CACHE_TIMEOUT: @@ -878,181 +853,25 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, return setopt_set_timeout_sec(&s->dns_cache_timeout_ms, arg); s->dns_cache_timeout_ms = -1; break; - - case CURLOPT_CA_CACHE_TIMEOUT: - if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) { - result = value_range(&arg, -1, -1, INT_MAX); - if(result) - return result; - - s->general_ssl.ca_cache_timeout = (int)arg; - } - else - return CURLE_NOT_BUILT_IN; - break; case CURLOPT_MAXCONNECTS: - result = value_range(&arg, 1, 1, UINT_MAX); - if(result) - return result; - s->maxconnects = (unsigned int)arg; + result = value_range(&arg, 1, 1, INT_MAX); + if(!result) + s->maxconnects = (uint32_t)arg; break; case CURLOPT_SERVER_RESPONSE_TIMEOUT: return setopt_set_timeout_sec(&s->server_response_timeout, arg); - case CURLOPT_SERVER_RESPONSE_TIMEOUT_MS: return setopt_set_timeout_ms(&s->server_response_timeout, arg); - -#ifndef CURL_DISABLE_TFTP - case CURLOPT_TFTP_BLKSIZE: - result = value_range(&arg, 0, TFTP_BLKSIZE_MIN, TFTP_BLKSIZE_MAX); - if(result) - return result; - s->tftp_blksize = (unsigned short)arg; - break; -#endif -#ifndef CURL_DISABLE_NETRC - case CURLOPT_NETRC: - if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->use_netrc = (unsigned char)arg; - break; -#endif - case CURLOPT_TIMECONDITION: - if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->timecondition = (unsigned char)arg; - break; - case CURLOPT_TIMEVALUE: - s->timevalue = (time_t)arg; - break; - case CURLOPT_SSLVERSION: -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLVERSION: -#endif - return Curl_setopt_SSLVERSION(data, option, arg); - - case CURLOPT_POSTFIELDSIZE: - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(s->postfieldsize < arg && - s->postfields == s->str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); - s->postfields = NULL; - } - - s->postfieldsize = arg; - break; -#ifndef CURL_DISABLE_HTTP - case CURLOPT_FOLLOWLOCATION: - if(uarg > 3) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->http_follow_mode = (unsigned char)uarg; - break; - - case CURLOPT_MAXREDIRS: - result = value_range(&arg, -1, -1, 0x7fff); - if(result) - return result; - s->maxredirs = (short)arg; - break; - - case CURLOPT_POSTREDIR: - if(arg < CURL_REDIR_GET_ALL) - /* no return error on too high numbers since the bitmask could be - extended in a future */ - return CURLE_BAD_FUNCTION_ARGUMENT; - s->keep_post = arg & CURL_REDIR_POST_ALL; - break; - - case CURLOPT_HEADEROPT: - s->sep_headers = !!(arg & CURLHEADER_SEPARATE); - break; - case CURLOPT_HTTPAUTH: - return httpauth(data, FALSE, uarg); - - case CURLOPT_HTTP_VERSION: - return setopt_HTTP_VERSION(data, arg); - - case CURLOPT_EXPECT_100_TIMEOUT_MS: - result = value_range(&arg, 0, 0, 0xffff); - if(result) - return result; - s->expect_100_timeout = (unsigned short)arg; - break; - -#endif /* !CURL_DISABLE_HTTP */ - -#ifndef CURL_DISABLE_MIME - case CURLOPT_MIME_OPTIONS: - s->mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); - break; -#endif -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYPORT: - if((arg < 0) || (arg > UINT16_MAX)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->proxyport = (uint16_t)arg; - break; - - case CURLOPT_PROXYAUTH: - return httpauth(data, TRUE, uarg); - - case CURLOPT_PROXYTYPE: - if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->proxytype = (unsigned char)arg; - break; - - case CURLOPT_SOCKS5_AUTH: - if(uarg & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) - return CURLE_NOT_BUILT_IN; - s->socks5auth = (unsigned char)uarg; - break; -#endif /* !CURL_DISABLE_PROXY */ - -#ifndef CURL_DISABLE_FTP - case CURLOPT_FTP_FILEMETHOD: - if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ftp_filemethod = (unsigned char)arg; - break; - case CURLOPT_FTP_SSL_CCC: - if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ftp_ccc = (unsigned char)arg; - break; - - case CURLOPT_FTPSSLAUTH: - if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ftpsslauth = (unsigned char)arg; - break; - case CURLOPT_ACCEPTTIMEOUT_MS: - return setopt_set_timeout_ms(&s->accepttimeout, arg); -#endif /* !CURL_DISABLE_FTP */ -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) - case CURLOPT_FTP_CREATE_MISSING_DIRS: - if((arg < CURLFTP_CREATE_DIR_NONE) || (arg > CURLFTP_CREATE_DIR_RETRY)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ftp_create_missing_dirs = (unsigned char)arg; - break; -#endif /* !CURL_DISABLE_FTP || USE_SSH */ - case CURLOPT_INFILESIZE: - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->filesize = arg; - break; case CURLOPT_LOW_SPEED_LIMIT: if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->low_speed_limit = arg; + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->low_speed_limit = arg; break; case CURLOPT_LOW_SPEED_TIME: - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->low_speed_time = arg; + result = value_range(&arg, 0, 0, USHRT_MAX); + if(!result) + s->low_speed_time = (uint16_t)arg; break; case CURLOPT_PORT: if((arg < 0) || (arg > 65535)) @@ -1061,22 +880,12 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, break; case CURLOPT_TIMEOUT: return setopt_set_timeout_sec(&s->timeout, arg); - case CURLOPT_TIMEOUT_MS: return setopt_set_timeout_ms(&s->timeout, arg); - case CURLOPT_CONNECTTIMEOUT: return setopt_set_timeout_sec(&s->connecttimeout, arg); - case CURLOPT_CONNECTTIMEOUT_MS: return setopt_set_timeout_ms(&s->connecttimeout, arg); - - case CURLOPT_RESUME_FROM: - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->set_resume_from = arg; - break; - #ifndef CURL_DISABLE_BINDLOCAL case CURLOPT_LOCALPORT: if((arg < 0) || (arg > 65535)) @@ -1089,158 +898,374 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, s->localportrange = curlx_sltous(arg); break; #endif - -#ifdef HAVE_GSSAPI - case CURLOPT_GSSAPI_DELEGATION: - s->gssapi_delegation = (unsigned char)uarg & - (CURLGSSAPI_DELEGATION_POLICY_FLAG | CURLGSSAPI_DELEGATION_FLAG); - break; -#endif - - case CURLOPT_SSL_FALSESTART: - return CURLE_NOT_BUILT_IN; case CURLOPT_BUFFERSIZE: result = value_range(&arg, 0, READBUFFER_MIN, READBUFFER_MAX); - if(result) - return result; - s->buffer_size = (unsigned int)arg; + if(!result) + s->buffer_size = (unsigned int)arg; break; - case CURLOPT_UPLOAD_BUFFERSIZE: result = value_range(&arg, 0, UPLOADBUFFER_MIN, UPLOADBUFFER_MAX); - if(result) - return result; - s->upload_buffer_size = (unsigned int)arg; + if(!result) + s->upload_buffer_size = (unsigned int)arg; break; - case CURLOPT_MAXFILESIZE: if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->max_filesize = arg; - break; - -#ifdef USE_SSL - case CURLOPT_USE_SSL: - if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->use_ssl = (unsigned char)arg; - break; - case CURLOPT_SSL_OPTIONS: - set_ssl_options(&s->ssl, &s->ssl.primary, arg); - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_OPTIONS: - set_ssl_options(&s->proxy_ssl, &s->proxy_ssl.primary, arg); + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->max_filesize = arg; break; -#endif - -#endif /* USE_SSL */ case CURLOPT_IPRESOLVE: if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->ipver = (unsigned char)arg; + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ipver = (unsigned char)arg; break; - case CURLOPT_CONNECT_ONLY: if(arg < 0 || arg > 2) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->connect_only = !!arg; - s->connect_only_ws = (arg == 2); - break; - -#ifdef USE_SSH - case CURLOPT_SSH_AUTH_TYPES: - s->ssh_auth_types = (int)arg; - break; -#endif - -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) - case CURLOPT_NEW_FILE_PERMS: - if((arg < 0) || (arg > 0777)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->new_file_perms = (unsigned int)arg; - break; -#endif -#ifdef USE_SSH - case CURLOPT_NEW_DIRECTORY_PERMS: - if((arg < 0) || (arg > 0777)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->new_directory_perms = (unsigned int)arg; + result = CURLE_BAD_FUNCTION_ARGUMENT; + else { + s->connect_only = !!arg; + s->connect_only_ws = (arg == 2); + } break; -#endif #ifdef USE_IPV6 case CURLOPT_ADDRESS_SCOPE: #if SIZEOF_LONG > 4 - if(uarg > UINT_MAX) - return CURLE_BAD_FUNCTION_ARGUMENT; + if((unsigned long)arg > UINT_MAX) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else #endif - s->scope_id = (unsigned int)uarg; + s->scope_id = (unsigned int)arg; break; #endif - case CURLOPT_PROTOCOLS: - s->allowed_protocols = (curl_prot_t)arg; + case CURLOPT_TCP_KEEPIDLE: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepidle = (int)arg; break; - - case CURLOPT_REDIR_PROTOCOLS: - s->redir_protocols = (curl_prot_t)arg; + case CURLOPT_TCP_KEEPINTVL: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepintvl = (int)arg; break; - -#ifndef CURL_DISABLE_RTSP - case CURLOPT_RTSP_REQUEST: - return setopt_RTSP_REQUEST(data, arg); - case CURLOPT_RTSP_CLIENT_CSEQ: - data->state.rtsp_next_client_CSeq = arg; + case CURLOPT_TCP_KEEPCNT: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepcnt = (int)arg; break; - - case CURLOPT_RTSP_SERVER_CSEQ: - data->state.rtsp_next_server_CSeq = arg; + case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: + return setopt_set_timeout_ms(&s->happy_eyeballs_timeout, arg); + case CURLOPT_UPKEEP_INTERVAL_MS: + return setopt_set_timeout_ms(&s->upkeep_interval_ms, arg); + case CURLOPT_MAXAGE_CONN: + return setopt_set_timeout_sec(&s->conn_max_idle_ms, arg); + case CURLOPT_MAXLIFETIME_CONN: + return setopt_set_timeout_sec(&s->conn_max_age_ms, arg); + case CURLOPT_DNS_USE_GLOBAL_CACHE: + /* deprecated */ break; + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} -#endif /* !CURL_DISABLE_RTSP */ - - case CURLOPT_TCP_KEEPIDLE: - result = value_range(&arg, 0, 0, INT_MAX); - if(result) - return result; - s->tcp_keepidle = (int)arg; +static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, + long arg) +{ +#ifdef USE_SSL + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_CA_CACHE_TIMEOUT: + if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) { + result = value_range(&arg, -1, -1, INT_MAX); + if(!result) + s->general_ssl.ca_cache_timeout = (int)arg; + } + else + result = CURLE_NOT_BUILT_IN; break; - case CURLOPT_TCP_KEEPINTVL: - result = value_range(&arg, 0, 0, INT_MAX); - if(result) - return result; - s->tcp_keepintvl = (int)arg; + case CURLOPT_SSLVERSION: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLVERSION: +#endif + return Curl_setopt_SSLVERSION(data, option, arg); + case CURLOPT_SSL_FALSESTART: + result = CURLE_NOT_BUILT_IN; break; - case CURLOPT_TCP_KEEPCNT: - result = value_range(&arg, 0, 0, INT_MAX); - if(result) - return result; - s->tcp_keepcnt = (int)arg; + case CURLOPT_USE_SSL: + if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->use_ssl = (unsigned char)arg; + break; + case CURLOPT_SSL_OPTIONS: + set_ssl_options(&s->ssl, &s->ssl.primary, arg); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSL_OPTIONS: + set_ssl_options(&s->proxy_ssl, &s->proxy_ssl.primary, arg); break; +#endif case CURLOPT_SSL_ENABLE_NPN: break; + case CURLOPT_SSLENGINE_DEFAULT: + Curl_safefree(s->str[STRING_SSL_ENGINE]); + result = Curl_ssl_set_engine_default(data); + break; + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +#else /* USE_SSL */ + (void)data; + (void)option; + (void)arg; + return CURLE_UNKNOWN_OPTION; +#endif /* !USE_SSL */ +} + +static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, + long arg) +{ +#ifndef CURL_DISABLE_PROXY + struct UserDefined *s = &data->set; + + switch(option) { + case CURLOPT_PROXYPORT: + if((arg < 0) || (arg > UINT16_MAX)) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->proxyport = (uint16_t)arg; + break; + case CURLOPT_PROXYAUTH: + return httpauth(data, TRUE, (unsigned long)arg); + case CURLOPT_PROXYTYPE: + if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->proxytype = (unsigned char)arg; + break; + case CURLOPT_SOCKS5_AUTH: + if(arg & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) + return CURLE_NOT_BUILT_IN; + s->socks5auth = (unsigned char)arg; + break; + default: + return CURLE_UNKNOWN_OPTION; + } + return CURLE_OK; +#else + (void)data; + (void)option; + (void)arg; + return CURLE_UNKNOWN_OPTION; +#endif +} + +static CURLcode setopt_long_http(struct Curl_easy *data, CURLoption option, + long arg) +{ +#ifndef CURL_DISABLE_HTTP + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { + case CURLOPT_FOLLOWLOCATION: + if((unsigned long)arg > 3) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->http_follow_mode = (unsigned char)arg; + break; + case CURLOPT_MAXREDIRS: + result = value_range(&arg, -1, -1, 0x7fff); + if(!result) + s->maxredirs = (short)arg; + break; + case CURLOPT_POSTREDIR: + if(arg < CURL_REDIR_GET_ALL) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else { + s->post301 = !!(arg & CURL_REDIR_POST_301); + s->post302 = !!(arg & CURL_REDIR_POST_302); + s->post303 = !!(arg & CURL_REDIR_POST_303); + } + break; + case CURLOPT_HEADEROPT: + s->sep_headers = !!(arg & CURLHEADER_SEPARATE); + break; + case CURLOPT_HTTPAUTH: + return httpauth(data, FALSE, (unsigned long)arg); + case CURLOPT_HTTP_VERSION: + return setopt_HTTP_VERSION(data, arg); + case CURLOPT_EXPECT_100_TIMEOUT_MS: + result = value_range(&arg, 0, 0, 0xffff); + if(!result) + s->expect_100_timeout = (unsigned short)arg; + break; case CURLOPT_STREAM_WEIGHT: #if defined(USE_HTTP2) || defined(USE_HTTP3) if((arg >= 1) && (arg <= 256)) s->priority.weight = (int)arg; break; #else - return CURLE_NOT_BUILT_IN; + result = CURLE_NOT_BUILT_IN; + break; #endif - case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: - return setopt_set_timeout_ms(&s->happy_eyeballs_timeout, arg); + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +#else + (void)data; + (void)option; + (void)arg; + return CURLE_UNKNOWN_OPTION; +#endif +} - case CURLOPT_UPKEEP_INTERVAL_MS: - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->upkeep_interval_ms = arg; +static CURLcode setopt_long_proto(struct Curl_easy *data, CURLoption option, + long arg) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { +#ifndef CURL_DISABLE_TFTP + case CURLOPT_TFTP_BLKSIZE: + result = value_range(&arg, 0, TFTP_BLKSIZE_MIN, TFTP_BLKSIZE_MAX); + if(!result) + s->tftp_blksize = (unsigned short)arg; break; - case CURLOPT_MAXAGE_CONN: - return setopt_set_timeout_sec(&s->conn_max_idle_ms, arg); +#endif +#ifndef CURL_DISABLE_NETRC + case CURLOPT_NETRC: + if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->use_netrc = (unsigned char)arg; + break; +#endif +#ifndef CURL_DISABLE_FTP + case CURLOPT_FTP_FILEMETHOD: + if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ftp_filemethod = (unsigned char)arg; + break; + case CURLOPT_FTP_SSL_CCC: + if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ftp_ccc = (unsigned char)arg; + break; + case CURLOPT_FTPSSLAUTH: + if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ftpsslauth = (unsigned char)arg; + break; + case CURLOPT_ACCEPTTIMEOUT_MS: + return setopt_set_timeout_ms(&s->accepttimeout, arg); +#endif +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + case CURLOPT_FTP_CREATE_MISSING_DIRS: + if((arg < CURLFTP_CREATE_DIR_NONE) || (arg > CURLFTP_CREATE_DIR_RETRY)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ftp_create_missing_dirs = (unsigned char)arg; + break; + case CURLOPT_NEW_FILE_PERMS: + if((arg < 0) || (arg > 0777)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->new_file_perms = (unsigned int)arg; + break; +#endif +#ifndef CURL_DISABLE_RTSP + case CURLOPT_RTSP_REQUEST: + return setopt_RTSP_REQUEST(data, arg); + case CURLOPT_RTSP_CLIENT_CSEQ: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + data->state.rtsp_next_client_CSeq = (uint32_t)arg; + break; + case CURLOPT_RTSP_SERVER_CSEQ: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + data->state.rtsp_next_server_CSeq = (uint32_t)arg; + break; +#endif +#ifdef USE_SSH + case CURLOPT_SSH_AUTH_TYPES: + s->ssh_auth_types = (uint32_t)arg; + break; + case CURLOPT_NEW_DIRECTORY_PERMS: + if((arg < 0) || (arg > 0777)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->new_directory_perms = (unsigned int)arg; + break; +#endif + case CURLOPT_PROTOCOLS: + s->allowed_protocols = (curl_prot_t)arg; + break; + case CURLOPT_REDIR_PROTOCOLS: + s->redir_protocols = (curl_prot_t)arg; + break; +#ifndef CURL_DISABLE_WEBSOCKETS + case CURLOPT_WS_OPTIONS: + s->ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE); + s->ws_no_auto_pong = (bool)(arg & CURLWS_NOAUTOPONG); + break; +#endif + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} - case CURLOPT_MAXLIFETIME_CONN: - return setopt_set_timeout_sec(&s->conn_max_age_ms, arg); +static CURLcode setopt_long_misc(struct Curl_easy *data, CURLoption option, + long arg) +{ + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_TIMECONDITION: + if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->timecondition = (unsigned char)arg; + break; + case CURLOPT_TIMEVALUE: + s->timevalue = (time_t)arg; + break; + case CURLOPT_POSTFIELDSIZE: + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + if(s->postfieldsize < arg && + s->postfields == s->str[STRING_COPYPOSTFIELDS]) { + Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); + s->postfields = NULL; + } + s->postfieldsize = arg; + break; + case CURLOPT_INFILESIZE: + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->filesize = arg; + break; + case CURLOPT_RESUME_FROM: + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + s->set_resume_from = arg; + break; + case CURLOPT_UPLOAD_FLAGS: + s->upload_flags = (unsigned char)arg; + break; +#ifndef CURL_DISABLE_MIME + case CURLOPT_MIME_OPTIONS: + s->mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); + break; +#endif #ifndef CURL_DISABLE_HSTS case CURLOPT_HSTS_CTRL: if(arg & CURLHSTS_ENABLE) { @@ -1253,41 +1278,47 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, else Curl_hsts_cleanup(&data->hsts); break; -#endif /* !CURL_DISABLE_HSTS */ +#endif #ifndef CURL_DISABLE_ALTSVC case CURLOPT_ALTSVC_CTRL: - if(!arg) { - DEBUGF(infof(data, "bad CURLOPT_ALTSVC_CTRL input")); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - if(!data->asi) { - data->asi = Curl_altsvc_init(); - if(!data->asi) - return CURLE_OUT_OF_MEMORY; - } - return Curl_altsvc_ctrl(data->asi, arg); -#endif /* !CURL_DISABLE_ALTSVC */ -#ifndef CURL_DISABLE_WEBSOCKETS - case CURLOPT_WS_OPTIONS: - s->ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE); - s->ws_no_auto_pong = (bool)(arg & CURLWS_NOAUTOPONG); - break; + return Curl_altsvc_ctrl(data, arg); #endif - case CURLOPT_DNS_USE_GLOBAL_CACHE: - /* deprecated */ - break; - case CURLOPT_SSLENGINE_DEFAULT: - Curl_safefree(s->str[STRING_SSL_ENGINE]); - return Curl_ssl_set_engine_default(data); - case CURLOPT_UPLOAD_FLAGS: - s->upload_flags = (unsigned char)arg; +#ifdef HAVE_GSSAPI + case CURLOPT_GSSAPI_DELEGATION: + s->gssapi_delegation = (unsigned char)arg & + (CURLGSSAPI_DELEGATION_POLICY_FLAG | CURLGSSAPI_DELEGATION_FLAG); break; +#endif default: return CURLE_UNKNOWN_OPTION; } return CURLE_OK; } +static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, + long arg) +{ + typedef CURLcode (*setoptfunc)(struct Curl_easy *data, + CURLoption option, long arg); + static const setoptfunc setopt_call[] = { + setopt_long_bool, + setopt_long_net, + setopt_long_http, + setopt_long_proxy, + setopt_long_ssl, + setopt_long_proto, + setopt_long_misc + }; + size_t i; + + for(i = 0; i < CURL_ARRAYSIZE(setopt_call); i++) { + CURLcode result = setopt_call[i](data, option, arg); + if(result != CURLE_UNKNOWN_OPTION) + return result; + } + return CURLE_UNKNOWN_OPTION; +} + static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option, struct curl_slist *slist) { @@ -1343,7 +1374,7 @@ static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option, * Entries added this way will remain in the cache until explicitly * removed or the handle is cleaned up. * - * Prefix the HOST with plus sign (+) to have the entry expire just like + * Prefix the HOST with plus sign (+) to have the entry expire like * automatically added entries. * * Prefix the HOST with dash (-) to _remove_ the entry from the cache. @@ -1385,6 +1416,38 @@ static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option, return result; } +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP) +#ifndef CURL_DISABLE_MIME +static CURLcode setopt_mimepost(struct Curl_easy *data, curl_mime *mimep) +{ + /* + * Set to make us do MIME POST + */ + CURLcode result; + struct UserDefined *s = &data->set; + if(!s->mimepostp) { + s->mimepostp = curlx_malloc(sizeof(*s->mimepostp)); + if(!s->mimepostp) + return CURLE_OUT_OF_MEMORY; + Curl_mime_initpart(s->mimepostp); + } + + result = Curl_mime_set_subparts(s->mimepostp, mimep, FALSE); + if(!result) { + s->method = HTTPREQ_POST_MIME; + s->opt_no_body = FALSE; /* this is implied */ +#ifndef CURL_DISABLE_FORM_API + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); + data->state.mimepost = NULL; +#endif + } + return result; +} +#endif /* !CURL_DISABLE_MIME */ +#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ + /* assorted pointer type arguments */ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, va_list param) @@ -1407,25 +1470,11 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, break; #endif /* !CURL_DISABLE_FORM_API */ #endif /* !CURL_DISABLE_HTTP */ -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_IMAP) -# ifndef CURL_DISABLE_MIME - case CURLOPT_MIMEPOST: - /* - * Set to make us do MIME POST - */ - result = Curl_mime_set_subparts(&s->mimepost, - va_arg(param, curl_mime *), - FALSE); - if(!result) { - s->method = HTTPREQ_POST_MIME; - s->opt_no_body = FALSE; /* this is implied */ -#ifndef CURL_DISABLE_FORM_API - Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); - data->state.mimepost = NULL; -#endif - } +#ifndef CURL_DISABLE_MIME + case CURLOPT_MIMEPOST: + result = setopt_mimepost(data, va_arg(param, curl_mime *)); break; #endif /* !CURL_DISABLE_MIME */ #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ @@ -1515,126 +1564,361 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, break; } #endif - + + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} + +#ifndef CURL_DISABLE_COOKIES +static CURLcode cookielist(struct Curl_easy *data, const char *ptr) +{ + CURLcode result = CURLE_OK; + if(!ptr) + return CURLE_OK; + + if(curl_strequal(ptr, "ALL")) { + /* clear all cookies */ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_clearall(data->cookies); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + else if(curl_strequal(ptr, "SESS")) { + /* clear session cookies */ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_clearsess(data->cookies); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + else if(curl_strequal(ptr, "FLUSH")) { + /* flush cookies to file, takes care of the locking */ + Curl_flush_cookies(data, FALSE); + } + else if(curl_strequal(ptr, "RELOAD")) { + /* reload cookies from file */ + return Curl_cookie_loadfiles(data); + } + else { + if(!data->cookies) { + /* if cookie engine was not running, activate it */ + data->cookies = Curl_cookie_init(); + if(!data->cookies) + return CURLE_OUT_OF_MEMORY; + data->state.cookie_engine = TRUE; + } + + /* general protection against mistakes and abuse */ + if(strlen(ptr) > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + if(checkprefix("Set-Cookie:", ptr)) + /* HTTP Header format line */ + result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, ptr + 11, + NULL, NULL, TRUE); + else + /* Netscape format line */ + result = Curl_cookie_add(data, data->cookies, FALSE, FALSE, ptr, NULL, + NULL, TRUE); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + return result; +} + +static CURLcode cookiefile(struct Curl_easy *data, const char *ptr) +{ + /* + * Set cookie file to read and parse. Can be used multiple times. + */ + if(ptr) { + struct curl_slist *cl; + /* general protection against mistakes and abuse */ + if(strlen(ptr) > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* append the cookie filename to the list of filenames, and deal with + them later */ + cl = curl_slist_append(data->state.cookielist, ptr); + if(!cl) { + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; + return CURLE_OUT_OF_MEMORY; + } + data->state.cookielist = cl; /* store the list for later use */ + } + else { + /* clear the list of cookie files */ + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; + + if(!data->share || !data->share->cookies) { + /* throw away all existing cookies if this is not a shared cookie + container */ + Curl_cookie_clearall(data->cookies); + Curl_cookie_cleanup(data->cookies); + } + /* disable the cookie engine */ + data->cookies = NULL; + } + return CURLE_OK; +} +#endif + +#ifndef CURL_DISABLE_PROXY +static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, + const char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_PROXYUSERPWD: { + /* + * user:password needed to use the proxy + */ + char *u = NULL; + char *p = NULL; + result = setstropt_userpwd(ptr, &u, &p); + + /* URL decode the components */ + if(!result && u) { + Curl_safefree(s->str[STRING_PROXYUSERNAME]); + result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + } + if(!result && p) { + Curl_safefree(s->str[STRING_PROXYPASSWORD]); + result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, + REJECT_ZERO); + } + curlx_free(u); + curlx_free(p); + break; + } + case CURLOPT_PROXYUSERNAME: + /* + * authentication username to use in the operation + */ + return Curl_setstropt(&s->str[STRING_PROXYUSERNAME], ptr); + + case CURLOPT_PROXYPASSWORD: + /* + * authentication password to use in the operation + */ + return Curl_setstropt(&s->str[STRING_PROXYPASSWORD], ptr); + + case CURLOPT_NOPROXY: + /* + * proxy exception list + */ + return Curl_setstropt(&s->str[STRING_NOPROXY], ptr); + case CURLOPT_PROXY_SSLCERT: + /* + * String that holds filename of the SSL certificate to use for proxy + */ + return Curl_setstropt(&s->str[STRING_CERT_PROXY], ptr); + case CURLOPT_PROXY_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use for proxy + */ + return Curl_setstropt(&s->str[STRING_CERT_TYPE_PROXY], ptr); + case CURLOPT_PROXY_SSLKEY: + /* + * String that holds filename of the SSL key to use for proxy + */ + return Curl_setstropt(&s->str[STRING_KEY_PROXY], ptr); + case CURLOPT_PROXY_KEYPASSWD: + /* + * String that holds the SSL private key password for proxy. + */ + return Curl_setstropt(&s->str[STRING_KEY_PASSWD_PROXY], ptr); + case CURLOPT_PROXY_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use for proxy + */ + return Curl_setstropt(&s->str[STRING_KEY_TYPE_PROXY], ptr); + case CURLOPT_PROXY_SSL_CIPHER_LIST: + if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { + /* set a list of cipher we want to use in the SSL connection for proxy */ + return Curl_setstropt(&s->str[STRING_SSL_CIPHER_LIST_PROXY], ptr); + } + else + return CURLE_NOT_BUILT_IN; + case CURLOPT_PROXY_TLS13_CIPHERS: + if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) + /* set preferred list of TLS 1.3 cipher suites for proxy */ + return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST_PROXY], ptr); + else + return CURLE_NOT_BUILT_IN; + case CURLOPT_PROXY: + /* + * Set proxy server:port to use as proxy. + * + * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) + * we explicitly say that we do not want to use a proxy + * (even though there might be environment variables saying so). + * + * Setting it to NULL, means no proxy but allows the environment variables + * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). + */ + return Curl_setstropt(&s->str[STRING_PROXY], ptr); + case CURLOPT_PRE_PROXY: + /* + * Set proxy server:port to use as SOCKS proxy. + * + * If the proxy is set to "" or NULL we explicitly say that we do not want + * to use the socks proxy. + */ + return Curl_setstropt(&s->str[STRING_PRE_PROXY], ptr); + case CURLOPT_SOCKS5_GSSAPI_SERVICE: + case CURLOPT_PROXY_SERVICE_NAME: + /* + * Set proxy authentication service name for Kerberos 5 and SPNEGO + */ + return Curl_setstropt(&s->str[STRING_PROXY_SERVICE_NAME], ptr); + case CURLOPT_PROXY_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY_PROXY], ptr); +#endif + return CURLE_NOT_BUILT_IN; + + case CURLOPT_HAPROXY_CLIENT_IP: + /* + * Set the client IP to send through HAProxy PROXY protocol + */ + result = Curl_setstropt(&s->str[STRING_HAPROXY_CLIENT_IP], ptr); + + /* enable the HAProxy protocol if an IP is provided */ + s->haproxyprotocol = !!s->str[STRING_HAPROXY_CLIENT_IP]; + break; + case CURLOPT_PROXY_CAINFO: + /* + * Set CA info SSL connection for proxy. Specify filename of the + * CA certificate + */ + s->proxy_ssl.custom_cafile = TRUE; + return Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); + case CURLOPT_PROXY_CRLFILE: + /* + * Set CRL file info for SSL connection for proxy. Specify filename of the + * CRL to check certificates revocation + */ + return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); + case CURLOPT_PROXY_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + case CURLOPT_PROXY_CAPATH: + /* + * Set CA path info for SSL connection proxy. Specify directory name of the + * CA certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { + /* This does not work on Windows. */ + s->proxy_ssl.custom_capath = TRUE; + return Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); + } +#endif + return CURLE_NOT_BUILT_IN; default: return CURLE_UNKNOWN_OPTION; } return result; } +#endif -#ifndef CURL_DISABLE_COOKIES -static CURLcode cookielist(struct Curl_easy *data, const char *ptr) +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) +/* + * A string with POST data. Makes curl HTTP POST. Even if it is NULL. If + * needed, CURLOPT_POSTFIELDSIZE must have been set prior to + * CURLOPT_COPYPOSTFIELDS and not altered later. + */ +static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) { CURLcode result = CURLE_OK; - if(!ptr) - return CURLE_OK; - - if(curl_strequal(ptr, "ALL")) { - /* clear all cookies */ - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_clearall(data->cookies); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - else if(curl_strequal(ptr, "SESS")) { - /* clear session cookies */ - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_clearsess(data->cookies); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - else if(curl_strequal(ptr, "FLUSH")) { - /* flush cookies to file, takes care of the locking */ - Curl_flush_cookies(data, FALSE); - } - else if(curl_strequal(ptr, "RELOAD")) { - /* reload cookies from file */ - return Curl_cookie_loadfiles(data); - } + if(!ptr || s->postfieldsize == -1) + result = Curl_setstropt(&s->str[STRING_COPYPOSTFIELDS], ptr); else { - if(!data->cookies) { - /* if cookie engine was not running, activate it */ - data->cookies = Curl_cookie_init(); - if(!data->cookies) - return CURLE_OUT_OF_MEMORY; - data->state.cookie_engine = TRUE; - } - - /* general protection against mistakes and abuse */ - if(strlen(ptr) > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - if(checkprefix("Set-Cookie:", ptr)) - /* HTTP Header format line */ - result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, ptr + 11, - NULL, NULL, TRUE); - else - /* Netscape format line */ - result = Curl_cookie_add(data, data->cookies, FALSE, FALSE, ptr, NULL, - NULL, TRUE); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - return result; -} + size_t pflen; -static CURLcode cookiefile(struct Curl_easy *data, const char *ptr) -{ - /* - * Set cookie file to read and parse. Can be used multiple times. - */ - if(ptr) { - struct curl_slist *cl; - /* general protection against mistakes and abuse */ - if(strlen(ptr) > CURL_MAX_INPUT_LENGTH) + if(s->postfieldsize < 0) return CURLE_BAD_FUNCTION_ARGUMENT; - /* append the cookie filename to the list of filenames, and deal with - them later */ - cl = curl_slist_append(data->state.cookielist, ptr); - if(!cl) { - curl_slist_free_all(data->state.cookielist); - data->state.cookielist = NULL; + pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); + if(pflen == SIZE_MAX) return CURLE_OUT_OF_MEMORY; + else { + /* Allocate even when size == 0. This satisfies the need of possible + later address compare to detect the COPYPOSTFIELDS mode, and to mark + that postfields is used rather than read function or form data. + */ + char *p = curlx_memdup0(ptr, pflen); + if(!p) + return CURLE_OUT_OF_MEMORY; + else { + curlx_free(s->str[STRING_COPYPOSTFIELDS]); + s->str[STRING_COPYPOSTFIELDS] = p; + } } - data->state.cookielist = cl; /* store the list for later use */ } - else { - /* clear the list of cookie files */ - curl_slist_free_all(data->state.cookielist); - data->state.cookielist = NULL; - if(!data->share || !data->share->cookies) { - /* throw away all existing cookies if this is not a shared cookie - container */ - Curl_cookie_clearall(data->cookies); - Curl_cookie_cleanup(data->cookies); - } - /* disable the cookie engine */ - data->cookies = NULL; - } - return CURLE_OK; + s->postfields = s->str[STRING_COPYPOSTFIELDS]; + s->method = HTTPREQ_POST; + return result; } #endif static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, char *ptr) { - CURLcode result = CURLE_OK; + CURLcode result; struct UserDefined *s = &data->set; +#ifndef CURL_DISABLE_PROXY + result = setopt_cptr_proxy(data, option, ptr); + if(result != CURLE_UNKNOWN_OPTION) + return result; +#endif + result = CURLE_OK; + switch(option) { + case CURLOPT_CAINFO: + /* + * Set CA info for SSL connection. Specify filename of the CA certificate + */ + s->ssl.custom_cafile = TRUE; + return Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); + case CURLOPT_CAPATH: + /* + * Set CA path info for SSL connection. Specify directory name of the CA + * certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { + /* This does not work on Windows. */ + s->ssl.custom_capath = TRUE; + return Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); + } +#endif + return CURLE_NOT_BUILT_IN; + case CURLOPT_CRLFILE: + /* + * Set CRL file info for SSL connection. Specify filename of the CRL + * to check certificates revocation + */ + if(Curl_ssl_supports(data, SSLSUPP_CRLFILE)) + return Curl_setstropt(&s->str[STRING_SSL_CRLFILE], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_SSL_CIPHER_LIST: if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) /* set a list of cipher we want to use in the SSL connection */ return Curl_setstropt(&s->str[STRING_SSL_CIPHER_LIST], ptr); else return CURLE_NOT_BUILT_IN; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_CIPHER_LIST: - if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { - /* set a list of cipher we want to use in the SSL connection for proxy */ - return Curl_setstropt(&s->str[STRING_SSL_CIPHER_LIST_PROXY], ptr); - } - else - return CURLE_NOT_BUILT_IN; -#endif case CURLOPT_TLS13_CIPHERS: if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { /* set preferred list of TLS 1.3 cipher suites */ @@ -1642,14 +1926,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, } else return CURLE_NOT_BUILT_IN; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) - /* set preferred list of TLS 1.3 cipher suites for proxy */ - return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST_PROXY], ptr); - else - return CURLE_NOT_BUILT_IN; -#endif case CURLOPT_RANDOM_FILE: break; case CURLOPT_EGDSOCKET: @@ -1666,40 +1942,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) case CURLOPT_COPYPOSTFIELDS: - /* - * A string with POST data. Makes curl HTTP POST. Even if it is NULL. - * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to - * CURLOPT_COPYPOSTFIELDS and not altered later. - */ - if(!ptr || s->postfieldsize == -1) - result = Curl_setstropt(&s->str[STRING_COPYPOSTFIELDS], ptr); - else { - size_t pflen; - - if(s->postfieldsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); - if(pflen == SIZE_MAX) - return CURLE_OUT_OF_MEMORY; - else { - /* Allocate even when size == 0. This satisfies the need of possible - later address compare to detect the COPYPOSTFIELDS mode, and to - mark that postfields is used rather than read function or form - data. - */ - char *p = Curl_memdup0(ptr, pflen); - if(!p) - return CURLE_OUT_OF_MEMORY; - else { - curlx_free(s->str[STRING_COPYPOSTFIELDS]); - s->str[STRING_COPYPOSTFIELDS] = p; - } - } - } - - s->postfields = s->str[STRING_COPYPOSTFIELDS]; - s->method = HTTPREQ_POST; - break; + return setopt_copypostfields(ptr, s); case CURLOPT_POSTFIELDS: /* @@ -1713,6 +1956,9 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ #ifndef CURL_DISABLE_HTTP + case CURLOPT_TRAILERDATA: + s->trailer_data = ptr; + break; case CURLOPT_ACCEPT_ENCODING: /* * String to use at the value of Accept-Encoding header. @@ -1804,43 +2050,9 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, */ return Curl_setstropt(&s->str[STRING_CUSTOMREQUEST], ptr); - /* we do not set - s->method = HTTPREQ_CUSTOM; - here, we continue as if we were using the already set type - and this just changes the actual request keyword */ - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY: - /* - * Set proxy server:port to use as proxy. - * - * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) - * we explicitly say that we do not want to use a proxy - * (even though there might be environment variables saying so). - * - * Setting it to NULL, means no proxy but allows the environment variables - * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). - */ - return Curl_setstropt(&s->str[STRING_PROXY], ptr); - - case CURLOPT_PRE_PROXY: - /* - * Set proxy server:port to use as SOCKS proxy. - * - * If the proxy is set to "" or NULL we explicitly say that we do not want - * to use the socks proxy. - */ - return Curl_setstropt(&s->str[STRING_PRE_PROXY], ptr); -#endif /* CURL_DISABLE_PROXY */ - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_SOCKS5_GSSAPI_SERVICE: - case CURLOPT_PROXY_SERVICE_NAME: - /* - * Set proxy authentication service name for Kerberos 5 and SPNEGO - */ - return Curl_setstropt(&s->str[STRING_PROXY_SERVICE_NAME], ptr); -#endif + /* we do not set s->method = HTTPREQ_CUSTOM; here, we continue as if we + were using the already set type and this changes the actual request + keyword */ case CURLOPT_SERVICE_NAME: /* * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO @@ -1928,15 +2140,9 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, */ s->closesocket_client = ptr; break; - case CURLOPT_TRAILERDATA: -#ifndef CURL_DISABLE_HTTP - s->trailer_data = ptr; -#endif - break; case CURLOPT_PREREQDATA: s->prereq_userp = ptr; break; - case CURLOPT_ERRORBUFFER: /* * Error buffer provided by the caller to get the human readable error @@ -1962,6 +2168,12 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, case CURLOPT_KRBLEVEL: return CURLE_NOT_BUILT_IN; /* removed in 8.17.0 */ + case CURLOPT_CHUNK_DATA: + s->wildcardptr = ptr; + break; + case CURLOPT_FNMATCH_DATA: + s->fnmatch_data = ptr; + break; #endif case CURLOPT_URL: /* @@ -1973,84 +2185,39 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, case CURLOPT_USERPWD: /* - * user:password to use in the operation - */ - return setstropt_userpwd(ptr, &s->str[STRING_USERNAME], - &s->str[STRING_PASSWORD]); - - case CURLOPT_USERNAME: - /* - * authentication username to use in the operation - */ - return Curl_setstropt(&s->str[STRING_USERNAME], ptr); - - case CURLOPT_PASSWORD: - /* - * authentication password to use in the operation - */ - return Curl_setstropt(&s->str[STRING_PASSWORD], ptr); - - case CURLOPT_LOGIN_OPTIONS: - /* - * authentication options to use in the operation - */ - return Curl_setstropt(&s->str[STRING_OPTIONS], ptr); - - case CURLOPT_XOAUTH2_BEARER: - /* - * OAuth 2.0 bearer token to use in the operation - */ - return Curl_setstropt(&s->str[STRING_BEARER], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYUSERPWD: { - /* - * user:password needed to use the proxy - */ - char *u = NULL; - char *p = NULL; - result = setstropt_userpwd(ptr, &u, &p); - - /* URL decode the components */ - if(!result && u) { - Curl_safefree(s->str[STRING_PROXYUSERNAME]); - result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, - REJECT_ZERO); - } - if(!result && p) { - Curl_safefree(s->str[STRING_PROXYPASSWORD]); - result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, - REJECT_ZERO); - } - curlx_free(u); - curlx_free(p); - break; - } - case CURLOPT_PROXYUSERNAME: + * user:password to use in the operation + */ + return setstropt_userpwd(ptr, &s->str[STRING_USERNAME], + &s->str[STRING_PASSWORD]); + + case CURLOPT_USERNAME: /* * authentication username to use in the operation */ - return Curl_setstropt(&s->str[STRING_PROXYUSERNAME], ptr); + return Curl_setstropt(&s->str[STRING_USERNAME], ptr); - case CURLOPT_PROXYPASSWORD: + case CURLOPT_PASSWORD: /* * authentication password to use in the operation */ - return Curl_setstropt(&s->str[STRING_PROXYPASSWORD], ptr); + return Curl_setstropt(&s->str[STRING_PASSWORD], ptr); - case CURLOPT_NOPROXY: + case CURLOPT_LOGIN_OPTIONS: /* - * proxy exception list + * authentication options to use in the operation */ - return Curl_setstropt(&s->str[STRING_NOPROXY], ptr); -#endif /* !CURL_DISABLE_PROXY */ + return Curl_setstropt(&s->str[STRING_OPTIONS], ptr); + case CURLOPT_XOAUTH2_BEARER: + /* + * OAuth 2.0 bearer token to use in the operation + */ + return Curl_setstropt(&s->str[STRING_BEARER], ptr); case CURLOPT_RANGE: /* * What range of the file you want to transfer */ return Curl_setstropt(&s->str[STRING_SET_RANGE], ptr); - case CURLOPT_CURLU: /* * pass CURLU to set URL @@ -2064,71 +2231,26 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * String that holds filename of the SSL certificate to use */ return Curl_setstropt(&s->str[STRING_CERT], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLCERT: - /* - * String that holds filename of the SSL certificate to use for proxy - */ - return Curl_setstropt(&s->str[STRING_CERT_PROXY], ptr); - -#endif case CURLOPT_SSLCERTTYPE: /* * String that holds file type of the SSL certificate to use */ return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use for proxy - */ - return Curl_setstropt(&s->str[STRING_CERT_TYPE_PROXY], ptr); - -#endif case CURLOPT_SSLKEY: /* * String that holds filename of the SSL key to use */ return Curl_setstropt(&s->str[STRING_KEY], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLKEY: - /* - * String that holds filename of the SSL key to use for proxy - */ - return Curl_setstropt(&s->str[STRING_KEY_PROXY], ptr); - -#endif case CURLOPT_SSLKEYTYPE: /* * String that holds file type of the SSL key to use */ return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use for proxy - */ - return Curl_setstropt(&s->str[STRING_KEY_TYPE_PROXY], ptr); - -#endif case CURLOPT_KEYPASSWD: /* * String that holds the SSL or SSH private key password. */ return Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_KEYPASSWD: - /* - * String that holds the SSL private key password for proxy. - */ - return Curl_setstropt(&s->str[STRING_KEY_PASSWD_PROXY], ptr); - -#endif case CURLOPT_SSLENGINE: /* * String that holds the SSL crypto engine. @@ -2140,19 +2262,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, } } break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_HAPROXY_CLIENT_IP: - /* - * Set the client IP to send through HAProxy PROXY protocol - */ - result = Curl_setstropt(&s->str[STRING_HAPROXY_CLIENT_IP], ptr); - - /* enable the HAProxy protocol if an IP is provided */ - s->haproxyprotocol = !!s->str[STRING_HAPROXY_CLIENT_IP]; - break; - -#endif case CURLOPT_INTERFACE: /* * Set what interface or address/hostname to bind the socket to when @@ -2162,122 +2271,29 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, &s->str[STRING_DEVICE], &s->str[STRING_INTERFACE], &s->str[STRING_BINDHOST]); - - case CURLOPT_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); -#endif - return CURLE_NOT_BUILT_IN; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY_PROXY], ptr); -#endif - return CURLE_NOT_BUILT_IN; -#endif - case CURLOPT_CAINFO: - /* - * Set CA info for SSL connection. Specify filename of the CA certificate - */ - s->ssl.custom_cafile = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CAINFO: - /* - * Set CA info SSL connection for proxy. Specify filename of the - * CA certificate - */ - s->proxy_ssl.custom_cafile = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); - -#endif - case CURLOPT_CAPATH: - /* - * Set CA path info for SSL connection. Specify directory name of the CA - * certificates which have been prepared using openssl c_rehash utility. - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { - /* This does not work on Windows. */ - s->ssl.custom_capath = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); - } -#endif - return CURLE_NOT_BUILT_IN; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CAPATH: - /* - * Set CA path info for SSL connection proxy. Specify directory name of the - * CA certificates which have been prepared using openssl c_rehash utility. - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { - /* This does not work on Windows. */ - s->proxy_ssl.custom_capath = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); - } -#endif - return CURLE_NOT_BUILT_IN; -#endif - case CURLOPT_CRLFILE: - /* - * Set CRL file info for SSL connection. Specify filename of the CRL - * to check certificates revocation - */ - return Curl_setstropt(&s->str[STRING_SSL_CRLFILE], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CRLFILE: - /* - * Set CRL file info for SSL connection for proxy. Specify filename of the - * CRL to check certificates revocation - */ - return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); - -#endif case CURLOPT_ISSUERCERT: /* * Set Issuer certificate file * to check certificates issuer */ - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); - -#endif + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_PRIVATE: /* * Set private data pointer. */ s->private_data = ptr; break; - #ifdef USE_SSL case CURLOPT_SSL_EC_CURVES: /* * Set accepted curves in SSL connection setup. * Specify colon-delimited list of curve algorithm names. */ - return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); - + if(Curl_ssl_supports(data, SSLSUPP_SSL_EC_CURVES)) + return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_SSL_SIGNATURE_ALGORITHMS: /* * Set accepted signature algorithms. @@ -2286,6 +2302,14 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) return Curl_setstropt(&s->str[STRING_SSL_SIGNATURE_ALGORITHMS], ptr); return CURLE_NOT_BUILT_IN; + case CURLOPT_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. + */ + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); + return CURLE_NOT_BUILT_IN; #endif #ifdef USE_SSH case CURLOPT_SSH_PUBLIC_KEYFILE: @@ -2293,13 +2317,17 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * Use this file instead of the $HOME/.ssh/id_dsa.pub file */ return Curl_setstropt(&s->str[STRING_SSH_PUBLIC_KEY], ptr); - case CURLOPT_SSH_PRIVATE_KEYFILE: /* * Use this file instead of the $HOME/.ssh/id_dsa file */ return Curl_setstropt(&s->str[STRING_SSH_PRIVATE_KEY], ptr); - + case CURLOPT_SSH_KEYDATA: + /* + * Custom client data to pass to the SSH keyfunc callback + */ + s->ssh_keyfunc_userp = ptr; + break; #if defined(USE_LIBSSH2) || defined(USE_LIBSSH) case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: /* @@ -2307,19 +2335,12 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * for validation purposes. */ return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); - case CURLOPT_SSH_KNOWNHOSTS: /* * Store the filename to read known hosts from. */ return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); #endif - case CURLOPT_SSH_KEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - s->ssh_keyfunc_userp = ptr; - break; #ifdef USE_LIBSSH2 case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: /* @@ -2327,7 +2348,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * for validation purposes. */ return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); - case CURLOPT_SSH_HOSTKEYDATA: /* * Custom client data to pass to the SSH keyfunc callback @@ -2345,9 +2365,8 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, } else /* make a NULL argument reset to default */ - s->allowed_protocols = (curl_prot_t)CURLPROTO_ALL; + s->allowed_protocols = (curl_prot_t)CURLPROTO_64ALL; break; - case CURLOPT_REDIR_PROTOCOLS_STR: if(ptr) { curl_prot_t protos; @@ -2359,16 +2378,13 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, /* make a NULL argument reset to default */ s->redir_protocols = (curl_prot_t)CURLPROTO_REDIR; break; - case CURLOPT_DEFAULT_PROTOCOL: /* Set the protocol to use when the URL does not include any protocol */ return Curl_setstropt(&s->str[STRING_DEFAULT_PROTOCOL], ptr); - #ifndef CURL_DISABLE_SMTP case CURLOPT_MAIL_FROM: /* Set the SMTP mail originator */ return Curl_setstropt(&s->str[STRING_MAIL_FROM], ptr); - case CURLOPT_MAIL_AUTH: /* Set the SMTP auth originator */ return Curl_setstropt(&s->str[STRING_MAIL_AUTH], ptr); @@ -2376,7 +2392,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, case CURLOPT_SASL_AUTHZID: /* Authorization identity (identity to act as) */ return Curl_setstropt(&s->str[STRING_SASL_AUTHZID], ptr); - #ifndef CURL_DISABLE_RTSP case CURLOPT_RTSP_SESSION_ID: /* @@ -2384,56 +2399,38 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * resuming a previously established RTSP session */ return Curl_setstropt(&s->str[STRING_RTSP_SESSION_ID], ptr); - case CURLOPT_RTSP_STREAM_URI: /* * Set the Stream URI for the RTSP request. Unless the request is * for generic server options, the application will need to set this. */ return Curl_setstropt(&s->str[STRING_RTSP_STREAM_URI], ptr); - case CURLOPT_RTSP_TRANSPORT: /* * The content of the Transport: header for the RTSP request */ return Curl_setstropt(&s->str[STRING_RTSP_TRANSPORT], ptr); - case CURLOPT_INTERLEAVEDATA: s->rtp_out = ptr; break; #endif /* !CURL_DISABLE_RTSP */ -#ifndef CURL_DISABLE_FTP - case CURLOPT_CHUNK_DATA: - s->wildcardptr = ptr; - break; - case CURLOPT_FNMATCH_DATA: - s->fnmatch_data = ptr; - break; -#endif #ifdef USE_TLS_SRP case CURLOPT_TLSAUTH_USERNAME: return Curl_setstropt(&s->str[STRING_TLSAUTH_USERNAME], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLSAUTH_USERNAME: - return Curl_setstropt(&s->str[STRING_TLSAUTH_USERNAME_PROXY], ptr); - -#endif case CURLOPT_TLSAUTH_PASSWORD: return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD], ptr); - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLSAUTH_PASSWORD: - return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD_PROXY], ptr); -#endif case CURLOPT_TLSAUTH_TYPE: if(ptr && !curl_strequal(ptr, "SRP")) - return CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; break; #ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_TLSAUTH_USERNAME: + return Curl_setstropt(&s->str[STRING_TLSAUTH_USERNAME_PROXY], ptr); + case CURLOPT_PROXY_TLSAUTH_PASSWORD: + return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD_PROXY], ptr); case CURLOPT_PROXY_TLSAUTH_TYPE: if(ptr && !curl_strequal(ptr, "SRP")) - return CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; break; #endif #endif @@ -2441,25 +2438,25 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, case CURLOPT_DNS_SERVERS: result = Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); if(result) - return result; + break; return Curl_async_ares_set_dns_servers(data); case CURLOPT_DNS_INTERFACE: result = Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); if(result) - return result; + break; return Curl_async_ares_set_dns_interface(data); case CURLOPT_DNS_LOCAL_IP4: result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); if(result) - return result; + break; return Curl_async_ares_set_dns_local_ip4(data); case CURLOPT_DNS_LOCAL_IP6: result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); if(result) - return result; + break; return Curl_async_ares_set_dns_local_ip6(data); #endif @@ -2529,7 +2526,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, } result = Curl_setstropt(&s->str[STRING_ALTSVC], ptr); if(result) - return result; + break; if(ptr) return Curl_altsvc_load(data->asi, ptr); break; @@ -2540,7 +2537,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, if(!ptr) { s->tls_ech = CURLECH_DISABLE; - return CURLE_OK; + break; } plen = strlen(ptr); if(plen > CURL_MAX_INPUT_LENGTH) { @@ -2558,15 +2555,11 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_HARD; else if(plen > 5 && !strncmp(ptr, "ecl:", 4)) { result = Curl_setstropt(&s->str[STRING_ECH_CONFIG], ptr + 4); - if(result) - return result; - s->tls_ech |= CURLECH_CLA_CFG; + if(!result) + s->tls_ech |= CURLECH_CLA_CFG; } - else if(plen > 4 && !strncmp(ptr, "pn:", 3)) { + else if(plen > 4 && !strncmp(ptr, "pn:", 3)) result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); - if(result) - return result; - } break; } #endif @@ -2907,7 +2900,9 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, /* * Blob that holds Issuer certificate to check certificates issuer */ - return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT], blob); + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT_BLOB)) + return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT], blob); + return CURLE_NOT_BUILT_IN; default: return CURLE_UNKNOWN_OPTION; @@ -2917,7 +2912,7 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, /* * Do not make Curl_vsetopt() static: it is called from - * packages/OS400/ccsidcurl.c. + * projects/OS400/ccsidcurl.c. */ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) { diff --git a/vendor/curl/lib/setopt.h b/vendor/curl/lib/setopt.h index 0dd60c7..c421f5c 100644 --- a/vendor/curl/lib/setopt.h +++ b/vendor/curl/lib/setopt.h @@ -34,7 +34,7 @@ CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option, CURLcode Curl_setstropt(char **charp, const char *s) WARN_UNUSED_RESULT; CURLcode Curl_setblobopt(struct curl_blob **blobp, const struct curl_blob *blob) WARN_UNUSED_RESULT; -CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg) +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) WARN_UNUSED_RESULT; #endif /* HEADER_CURL_SETOPT_H */ diff --git a/vendor/curl/lib/setup-os400.h b/vendor/curl/lib/setup-os400.h index 4cb0d89..084cd9f 100644 --- a/vendor/curl/lib/setup-os400.h +++ b/vendor/curl/lib/setup-os400.h @@ -30,9 +30,6 @@ /* OS/400 netdb.h does not define NI_MAXSERV. */ #define NI_MAXSERV 32 -/* No OS/400 header file defines u_int32_t. */ -typedef unsigned long u_int32_t; - /* OS/400 has no idea of a tty! */ #define isatty(fd) 0 @@ -42,7 +39,7 @@ typedef unsigned long u_int32_t; #include /* Be sure it is loaded. */ #undef puts -#define puts(s) (fputs((s), stdout) == EOF ? EOF : putchar('\n')) +#define puts(s) (fputs(s, stdout) == EOF ? EOF : putchar('\n')) /* System API wrapper prototypes & definitions to support ASCII parameters. */ diff --git a/vendor/curl/lib/setup-vms.h b/vendor/curl/lib/setup-vms.h index dd46022..35d12f0 100644 --- a/vendor/curl/lib/setup-vms.h +++ b/vendor/curl/lib/setup-vms.h @@ -24,11 +24,10 @@ * ***************************************************************************/ -/* */ -/* JEM, 12/30/12, VMS now generates config.h, so only define wrappers for */ -/* getenv(), getpwuid() and provide is_vms_shell() */ -/* Also need upper case symbols for system services, and */ -/* OpenSSL, and some Kerberos image */ +/* JEM, 2012-12-30, VMS now generates config.h, so only define wrappers for */ +/* getenv(), getpwuid() and provide is_vms_shell() */ +/* Also need upper case symbols for system services, and */ +/* OpenSSL, and some Kerberos image */ #ifdef __DECC #pragma message save @@ -87,7 +86,7 @@ static char *vms_translate_path(const char *path) char *test_str; /* See if the result is in VMS format, if not, we are done */ - /* Assume that this is a PATH, not just some data */ + /* Assume that this is a PATH, not some data */ test_str = strpbrk(path, ":[<^"); if(!test_str) { return (char *)path; @@ -166,7 +165,7 @@ static struct passwd *vms_getpwuid(uid_t uid) return my_passwd; } - /* If no changes needed just return it */ + /* If no changes needed, return it */ if(unix_path == my_passwd->pw_dir) { return my_passwd; } diff --git a/vendor/curl/lib/setup-win32.h b/vendor/curl/lib/setup-win32.h index 02177a7..6a89b96 100644 --- a/vendor/curl/lib/setup-win32.h +++ b/vendor/curl/lib/setup-win32.h @@ -45,31 +45,32 @@ /* Define to use BSD-style lwIP TCP/IP stack. */ /* #define USE_LWIPSOCK 1 */ # undef HAVE_GETHOSTNAME -# undef LWIP_POSIX_SOCKETS_IO_NAMES -# undef RECV_TYPE_ARG1 -# undef RECV_TYPE_ARG3 -# undef SEND_TYPE_ARG1 -# undef SEND_TYPE_ARG3 # define HAVE_GETHOSTBYNAME_R # define HAVE_GETHOSTBYNAME_R_6 +# undef LWIP_POSIX_SOCKETS_IO_NAMES # define LWIP_POSIX_SOCKETS_IO_NAMES 0 +# undef RECV_TYPE_ARG1 # define RECV_TYPE_ARG1 int +# undef RECV_TYPE_ARG3 # define RECV_TYPE_ARG3 size_t +# undef SEND_TYPE_ARG1 # define SEND_TYPE_ARG1 int +# undef SEND_TYPE_ARG3 # define SEND_TYPE_ARG3 size_t #elif defined(_WIN32) # define USE_WINSOCK 2 +# include +# include #endif /* - * Include header files for Windows builds before redefining anything. - * Use this preprocessor block only to include or exclude windows.h, - * winsock2.h or ws2tcpip.h. Any other Windows thing belongs - * to any other further and independent block. Under Cygwin things work - * just as under Linux (e.g. ) and the Winsock headers should - * never be included when __CYGWIN__ is defined. + * Include header files for Windows builds before redefining anything. Use + * this preprocessor block only to include or exclude windows.h, winsock2.h or + * ws2tcpip.h. Any other Windows thing belongs to any other further and + * independent block. Under Cygwin things work as under Linux (e.g. + * ) and the Winsock headers should never be included when + * __CYGWIN__ is defined. */ - #ifdef _WIN32 # if defined(UNICODE) && !defined(_UNICODE) # error "UNICODE is defined but _UNICODE is not defined" @@ -77,8 +78,6 @@ # if defined(_UNICODE) && !defined(UNICODE) # error "_UNICODE is defined but UNICODE is not defined" # endif -# include -# include # include # include # include @@ -89,13 +88,6 @@ * those symbols to compare against, and even those that do may be missing * newer symbols. */ - -#ifndef _WIN32_WINNT_WINXP -#define _WIN32_WINNT_WINXP 0x0501 /* Windows XP */ -#endif -#ifndef _WIN32_WINNT_WS03 -#define _WIN32_WINNT_WS03 0x0502 /* Windows Server 2003 */ -#endif #ifndef _WIN32_WINNT_VISTA #define _WIN32_WINNT_VISTA 0x0600 /* Windows Vista */ #endif diff --git a/vendor/curl/lib/sha256.c b/vendor/curl/lib/sha256.c index 7c3cad3..7b06365 100644 --- a/vendor/curl/lib/sha256.c +++ b/vendor/curl/lib/sha256.c @@ -30,30 +30,11 @@ #include "curl_sha256.h" #ifdef USE_MBEDTLS - #include - #if MBEDTLS_VERSION_NUMBER < 0x03020000 - #error "mbedTLS 3.2.0 or later required" - #endif - #include - #if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ - #define USE_MBEDTLS_SHA256 - #endif +#include +#if MBEDTLS_VERSION_NUMBER < 0x03020000 +#error "mbedTLS 3.2.0 or later required" #endif - -#ifdef USE_OPENSSL -#include -#elif defined(USE_GNUTLS) -#include -#elif defined(USE_MBEDTLS_SHA256) -#include -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) -#include -#define AN_APPLE_OS -#elif defined(USE_WIN32_CRYPTO) -#include +#include #endif /* Please keep the SSL backend-specific #if branches in this order: @@ -69,6 +50,7 @@ */ #ifdef USE_OPENSSL +#include struct ossl_sha256_ctx { EVP_MD_CTX *openssl_ctx; @@ -105,6 +87,7 @@ static void my_sha256_final(unsigned char *digest, void *in) } #elif defined(USE_GNUTLS) +#include typedef struct sha256_ctx my_sha256_ctx; @@ -126,7 +109,9 @@ static void my_sha256_final(unsigned char *digest, void *ctx) sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); } -#elif defined(USE_MBEDTLS_SHA256) +#elif defined(USE_MBEDTLS) && \ + defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ +#include typedef psa_hash_operation_t my_sha256_ctx; @@ -152,7 +137,12 @@ static void my_sha256_final(unsigned char *digest, void *ctx) &actual_length); } -#elif defined(AN_APPLE_OS) +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) +#include + typedef CC_SHA256_CTX my_sha256_ctx; static CURLcode my_sha256_init(void *ctx) @@ -174,6 +164,7 @@ static void my_sha256_final(unsigned char *digest, void *ctx) } #elif defined(USE_WIN32_CRYPTO) +#include struct sha256_ctx { HCRYPTPROV hCryptProv; @@ -181,11 +172,6 @@ struct sha256_ctx { }; typedef struct sha256_ctx my_sha256_ctx; -/* Offered when targeting Vista (XP SP2+) */ -#ifndef CALG_SHA_256 -#define CALG_SHA_256 0x0000800c -#endif - static CURLcode my_sha256_init(void *in) { my_sha256_ctx *ctx = (my_sha256_ctx *)in; @@ -246,38 +232,20 @@ static void my_sha256_final(unsigned char *digest, void *in) (a)[3] = (unsigned char) (((unsigned long)(val)) & 0xff); \ } while(0) -#ifdef HAVE_LONGLONG -#define WPA_PUT_BE64(a, val) \ - do { \ - (a)[0] = (unsigned char)(((unsigned long long)(val)) >> 56); \ - (a)[1] = (unsigned char)(((unsigned long long)(val)) >> 48); \ - (a)[2] = (unsigned char)(((unsigned long long)(val)) >> 40); \ - (a)[3] = (unsigned char)(((unsigned long long)(val)) >> 32); \ - (a)[4] = (unsigned char)(((unsigned long long)(val)) >> 24); \ - (a)[5] = (unsigned char)(((unsigned long long)(val)) >> 16); \ - (a)[6] = (unsigned char)(((unsigned long long)(val)) >> 8); \ - (a)[7] = (unsigned char)(((unsigned long long)(val)) & 0xff); \ - } while(0) -#else -#define WPA_PUT_BE64(a, val) \ - do { \ - (a)[0] = (unsigned char)(((unsigned __int64)(val)) >> 56); \ - (a)[1] = (unsigned char)(((unsigned __int64)(val)) >> 48); \ - (a)[2] = (unsigned char)(((unsigned __int64)(val)) >> 40); \ - (a)[3] = (unsigned char)(((unsigned __int64)(val)) >> 32); \ - (a)[4] = (unsigned char)(((unsigned __int64)(val)) >> 24); \ - (a)[5] = (unsigned char)(((unsigned __int64)(val)) >> 16); \ - (a)[6] = (unsigned char)(((unsigned __int64)(val)) >> 8); \ - (a)[7] = (unsigned char)(((unsigned __int64)(val)) & 0xff); \ +#define WPA_PUT_BE64(a, val) \ + do { \ + (a)[0] = (unsigned char)(((uint64_t)(val)) >> 56); \ + (a)[1] = (unsigned char)(((uint64_t)(val)) >> 48); \ + (a)[2] = (unsigned char)(((uint64_t)(val)) >> 40); \ + (a)[3] = (unsigned char)(((uint64_t)(val)) >> 32); \ + (a)[4] = (unsigned char)(((uint64_t)(val)) >> 24); \ + (a)[5] = (unsigned char)(((uint64_t)(val)) >> 16); \ + (a)[6] = (unsigned char)(((uint64_t)(val)) >> 8); \ + (a)[7] = (unsigned char)(((uint64_t)(val)) & 0xff); \ } while(0) -#endif struct sha256_state { -#ifdef HAVE_LONGLONG - unsigned long long length; -#else - unsigned __int64 length; -#endif + uint64_t length; unsigned long state[8], curlen; unsigned char buf[64]; }; @@ -307,7 +275,7 @@ static const unsigned long K[64] = { #define Sha256_Ch(x, y, z) (z ^ (x & (y ^ z))) #define Sha256_Maj(x, y, z) (((x | y) & z) | (x & y)) -#define Sha256_S(x, n) RORc((x), (n)) +#define Sha256_S(x, n) RORc(x, n) #define Sha256_R(x, n) (((x) & 0xFFFFFFFFUL) >> (n)) #define Sigma0(x) (Sha256_S(x, 2) ^ Sha256_S(x, 13) ^ Sha256_S(x, 22)) @@ -315,7 +283,7 @@ static const unsigned long K[64] = { #define Gamma0(x) (Sha256_S(x, 7) ^ Sha256_S(x, 18) ^ Sha256_R(x, 3)) #define Gamma1(x) (Sha256_S(x, 17) ^ Sha256_S(x, 19) ^ Sha256_R(x, 10)) -/* Compress 512-bits */ +/* Compress 512 bits */ static int sha256_compress(struct sha256_state *md, const unsigned char *buf) { unsigned long S[8], W[64]; @@ -325,7 +293,7 @@ static int sha256_compress(struct sha256_state *md, const unsigned char *buf) for(i = 0; i < 8; i++) { S[i] = md->state[i]; } - /* copy the state into 512-bits into W[0..15] */ + /* copy the state into 512 bits into W[0..15] */ for(i = 0; i < 16; i++) W[i] = WPA_GET_BE32(buf + (4 * i)); /* fill W[16..63] */ @@ -448,7 +416,7 @@ static void my_sha256_final(unsigned char *out, void *ctx) */ if(md->curlen > 56) { while(md->curlen < 64) { - md->buf[md->curlen++] = (unsigned char)0; + md->buf[md->curlen++] = 0; } sha256_compress(md, md->buf); md->curlen = 0; @@ -456,7 +424,7 @@ static void my_sha256_final(unsigned char *out, void *ctx) /* Pad up to 56 bytes of zeroes */ while(md->curlen < 56) { - md->buf[md->curlen++] = (unsigned char)0; + md->buf[md->curlen++] = 0; } /* Store length */ @@ -484,14 +452,14 @@ static void my_sha256_final(unsigned char *out, void *ctx) * Returns CURLE_OK on success. */ CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, - const size_t length) + const size_t len) { CURLcode result; my_sha256_ctx ctx; result = my_sha256_init(&ctx); if(!result) { - my_sha256_update(&ctx, input, curlx_uztoui(length)); + my_sha256_update(&ctx, input, curlx_uztoui(len)); my_sha256_final(output, &ctx); } return result; diff --git a/vendor/curl/lib/sigpipe.h b/vendor/curl/lib/sigpipe.h index 5d2c78a..b489796 100644 --- a/vendor/curl/lib/sigpipe.h +++ b/vendor/curl/lib/sigpipe.h @@ -25,19 +25,15 @@ ***************************************************************************/ #include "curl_setup.h" -#if defined(HAVE_SIGACTION) && \ - (defined(USE_OPENSSL) || defined(USE_MBEDTLS) || defined(USE_WOLFSSL)) +#if defined(HAVE_SIGACTION) && !defined(USE_SO_NOSIGPIPE) #include -struct sigpipe_ignore { +struct Curl_sigpipe_ctx { struct sigaction old_pipe_act; BIT(no_signal); }; -#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x -#define SIGPIPE_MEMBER(x) struct sigpipe_ignore x - -static void sigpipe_init(struct sigpipe_ignore *ig) +static CURL_INLINE void sigpipe_init(struct Curl_sigpipe_ctx *ig) { memset(ig, 0, sizeof(*ig)); ig->no_signal = TRUE; @@ -48,8 +44,8 @@ static void sigpipe_init(struct sigpipe_ignore *ig) * internals, and then sigpipe_restore() will restore the situation when we * return from libcurl again. */ -static void sigpipe_ignore(struct Curl_easy *data, - struct sigpipe_ignore *ig) +static CURL_INLINE void sigpipe_ignore(struct Curl_easy *data, + struct Curl_sigpipe_ctx *ig) { /* get a local copy of no_signal because the Curl_easy might not be around when we restore */ @@ -61,6 +57,10 @@ static void sigpipe_ignore(struct Curl_easy *data, action = ig->old_pipe_act; /* ignore this signal */ action.sa_handler = SIG_IGN; +#ifdef SA_SIGINFO + /* clear SA_SIGINFO flag since we are using sa_handler */ + action.sa_flags &= ~SA_SIGINFO; +#endif sigaction(SIGPIPE, &action, NULL); } } @@ -70,30 +70,33 @@ static void sigpipe_ignore(struct Curl_easy *data, * and SIGPIPE handling. It MUST only be called after a corresponding * sigpipe_ignore() was used. */ -static void sigpipe_restore(struct sigpipe_ignore *ig) +static CURL_INLINE void sigpipe_restore(struct Curl_sigpipe_ctx *ig) { if(!ig->no_signal) /* restore the outside state */ sigaction(SIGPIPE, &ig->old_pipe_act, NULL); } -static void sigpipe_apply(struct Curl_easy *data, - struct sigpipe_ignore *ig) +static CURL_INLINE void sigpipe_apply(struct Curl_easy *data, + struct Curl_sigpipe_ctx *ig) { - if(data->set.no_signal != ig->no_signal) { + if(data && (data->set.no_signal != ig->no_signal)) { sigpipe_restore(ig); sigpipe_ignore(data, ig); } } -#else -/* for systems without sigaction */ -#define sigpipe_ignore(x, y) Curl_nop_stmt -#define sigpipe_apply(x, y) Curl_nop_stmt -#define sigpipe_init(x) Curl_nop_stmt -#define sigpipe_restore(x) Curl_nop_stmt -#define SIGPIPE_VARIABLE(x) -#define SIGPIPE_MEMBER(x) bool x -#endif +#else /* !HAVE_SIGACTION || USE_SO_NOSIGPIPE */ +/* for systems without sigaction or where SO_NOSIGPIPE is used. */ +#define sigpipe_ignore(x, y) do { (void)(x); (void)(y); } while(0) +#define sigpipe_apply(x, y) do { (void)(x); (void)(y); } while(0) +#define sigpipe_init(x) do { (void)(x); } while(0) +#define sigpipe_restore(x) do { (void)(x); } while(0) + +struct Curl_sigpipe_ctx { + bool dummy; +}; + +#endif /* else HAVE_SIGACTION && !USE_SO_NOSIGPIPE */ #endif /* HEADER_CURL_SIGPIPE_H */ diff --git a/vendor/curl/lib/slist.c b/vendor/curl/lib/slist.c index 9bfd08a..c6dcdc1 100644 --- a/vendor/curl/lib/slist.c +++ b/vendor/curl/lib/slist.c @@ -79,7 +79,7 @@ struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, * curl_slist_append() appends a string to the linked list. It always returns * the address of the first record, so that you can use this function as an * initialization function as well as an append function. If you find this - * bothersome, then simply create a separate _init function and call it + * bothersome, then create a separate _init function and call it * appropriately from within the program. */ struct curl_slist *curl_slist_append(struct curl_slist *list, const char *data) diff --git a/vendor/curl/lib/smb.c b/vendor/curl/lib/smb.c index 003a38f..00297ad 100644 --- a/vendor/curl/lib/smb.c +++ b/vendor/curl/lib/smb.c @@ -23,11 +23,15 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#ifdef HAVE_ARPA_INET_H +#include /* for htons() */ +#endif + #include "smb.h" -#include "urldata.h" #include "url.h" #include "sendf.h" #include "curl_trc.h" @@ -285,77 +289,6 @@ struct smb_tree_disconnect { # pragma pack(pop) #endif -/* Local API functions */ -static CURLcode smb_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode smb_connect(struct Curl_easy *data, bool *done); -static CURLcode smb_connection_state(struct Curl_easy *data, bool *done); -static CURLcode smb_do(struct Curl_easy *data, bool *done); -static CURLcode smb_request_state(struct Curl_easy *data, bool *done); -static CURLcode smb_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode smb_parse_url_path(struct Curl_easy *data, - struct smb_conn *smbc, - struct smb_request *req); - -/* - * SMB handler interface - */ -const struct Curl_handler Curl_handler_smb = { - "smb", /* scheme */ - smb_setup_connection, /* setup_connection */ - smb_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - smb_connect, /* connect_it */ - smb_connection_state, /* connecting */ - smb_request_state, /* doing */ - smb_pollset, /* proto_pollset */ - smb_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SMB, /* defport */ - CURLPROTO_SMB, /* protocol */ - CURLPROTO_SMB, /* family */ - PROTOPT_CONN_REUSE /* flags */ -}; - -#ifdef USE_SSL -/* - * SMBS handler interface - */ -const struct Curl_handler Curl_handler_smbs = { - "smbs", /* scheme */ - smb_setup_connection, /* setup_connection */ - smb_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - smb_connect, /* connect_it */ - smb_connection_state, /* connecting */ - smb_request_state, /* doing */ - smb_pollset, /* proto_pollset */ - smb_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SMBS, /* defport */ - CURLPROTO_SMBS, /* protocol */ - CURLPROTO_SMB, /* family */ - PROTOPT_SSL | PROTOPT_CONN_REUSE /* flags */ -}; -#endif - #define MAX_PAYLOAD_SIZE 0x8000 #define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000) #define CLIENTNAME "curl" @@ -390,7 +323,7 @@ static curl_off_t smb_swap64(curl_off_t x) static void conn_state(struct Curl_easy *data, struct smb_conn *smbc, enum smb_conn_state newstate) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* For debug purposes */ static const char * const names[] = { "SMB_NOT_CONNECTED", @@ -404,8 +337,9 @@ static void conn_state(struct Curl_easy *data, struct smb_conn *smbc, if(smbc->state != newstate) infof(data, "SMB conn %p state change from %s to %s", (void *)smbc, names[smbc->state], names[newstate]); -#endif +#else (void)data; +#endif smbc->state = newstate; } @@ -414,7 +348,7 @@ static void request_state(struct Curl_easy *data, { struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY); if(req) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) /* For debug purposes */ static const char * const names[] = { "SMB_REQUESTING", @@ -442,9 +376,7 @@ static void smb_easy_dtor(void *key, size_t klen, void *entry) struct smb_request *req = entry; (void)key; (void)klen; - /* `req->path` points to somewhere in `struct smb_conn` which is - * kept at the connection meta. If the connection is destroyed first, - * req->path points to free'd memory. */ + Curl_safefree(req->path); curlx_free(req); } @@ -460,6 +392,53 @@ static void smb_conn_dtor(void *key, size_t klen, void *entry) curlx_free(smbc); } +static CURLcode smb_parse_url_path(struct Curl_easy *data, + struct smb_conn *smbc, + struct smb_request *req) +{ + char *path; + char *slash, *s; + CURLcode result; + + /* URL decode the path */ + result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); + if(result) + return result; + + /* Parse the path for the share */ + Curl_safefree(smbc->share); + smbc->share = curlx_strdup((*path == '/' || *path == '\\') + ? path + 1 : path); + curlx_free(path); + if(!smbc->share) + return CURLE_OUT_OF_MEMORY; + + slash = strchr(smbc->share, '/'); + if(!slash) + slash = strchr(smbc->share, '\\'); + + /* The share must be present */ + if(!slash) { + Curl_safefree(smbc->share); + failf(data, "missing share in URL path for SMB"); + return CURLE_URL_MALFORMAT; + } + + /* Parse the path for the file path converting any forward slashes into + backslashes */ + *slash++ = 0; + for(s = slash; *s; s++) { + if(*s == '/') + *s = '\\'; + } + /* keep a copy at easy struct to not share this with connection state */ + req->path = curlx_strdup(slash); + if(!req->path) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + /* this should setup things in the connection, not in the easy handle */ static CURLcode smb_setup_connection(struct Curl_easy *data, @@ -571,7 +550,7 @@ static CURLcode smb_recv_message(struct Curl_easy *data, if(nbt_size >= msg_size + 1) { /* Add the word count */ - msg_size += 1 + ((unsigned char)buf[msg_size]) * sizeof(unsigned short); + msg_size += 1 + (((unsigned char)buf[msg_size]) * sizeof(unsigned short)); if(nbt_size >= msg_size + sizeof(unsigned short)) { /* Add the byte count */ msg_size += sizeof(unsigned short) + @@ -1226,47 +1205,60 @@ static CURLcode smb_do(struct Curl_easy *data, bool *done) return CURLE_URL_MALFORMAT; } -static CURLcode smb_parse_url_path(struct Curl_easy *data, - struct smb_conn *smbc, - struct smb_request *req) -{ - char *path; - char *slash; - CURLcode result; - - /* URL decode the path */ - result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); - if(result) - return result; - - /* Parse the path for the share */ - smbc->share = curlx_strdup((*path == '/' || *path == '\\') - ? path + 1 : path); - curlx_free(path); - if(!smbc->share) - return CURLE_OUT_OF_MEMORY; - - slash = strchr(smbc->share, '/'); - if(!slash) - slash = strchr(smbc->share, '\\'); - - /* The share must be present */ - if(!slash) { - Curl_safefree(smbc->share); - failf(data, "missing share in URL path for SMB"); - return CURLE_URL_MALFORMAT; - } +/* + * SMB handler interface + */ +static const struct Curl_protocol Curl_protocol_smb = { + smb_setup_connection, /* setup_connection */ + smb_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + smb_connect, /* connect_it */ + smb_connection_state, /* connecting */ + smb_request_state, /* doing */ + smb_pollset, /* proto_pollset */ + smb_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; - /* Parse the path for the file path converting any forward slashes into - backslashes */ - *slash++ = 0; - req->path = slash; +#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ - for(; *slash; slash++) { - if(*slash == '/') - *slash = '\\'; - } - return CURLE_OK; -} +/* + * SMB handler interface + */ +const struct Curl_scheme Curl_scheme_smb = { + "smb", /* scheme */ +#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE) + ZERO_NULL, +#else + &Curl_protocol_smb, +#endif + CURLPROTO_SMB, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_CONN_REUSE, /* flags */ + PORT_SMB, /* defport */ +}; -#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ +/* + * SMBS handler interface + */ +const struct Curl_scheme Curl_scheme_smbs = { + "smbs", /* scheme */ +#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE) || \ + !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_smb, +#endif + CURLPROTO_SMBS, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_SSL | PROTOPT_CONN_REUSE, /* flags */ + PORT_SMBS, /* defport */ +}; diff --git a/vendor/curl/lib/smb.h b/vendor/curl/lib/smb.h index 474f8d6..d375404 100644 --- a/vendor/curl/lib/smb.h +++ b/vendor/curl/lib/smb.h @@ -24,12 +24,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - -extern const struct Curl_handler Curl_handler_smb; -extern const struct Curl_handler Curl_handler_smbs; - -#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ +extern const struct Curl_scheme Curl_scheme_smb; +extern const struct Curl_scheme Curl_scheme_smbs; #endif /* HEADER_CURL_SMB_H */ diff --git a/vendor/curl/lib/smtp.c b/vendor/curl/lib/smtp.c index 471c823..6088985 100644 --- a/vendor/curl/lib/smtp.c +++ b/vendor/curl/lib/smtp.c @@ -39,6 +39,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "smtp.h" #ifndef CURL_DISABLE_SMTP @@ -56,7 +58,6 @@ #include #endif -#include "urldata.h" #include "sendf.h" #include "curl_trc.h" #include "hostip.h" @@ -65,7 +66,6 @@ #include "escape.h" #include "pingpong.h" #include "mime.h" -#include "smtp.h" #include "vtls/vtls.h" #include "cfilters.h" #include "connect.h" @@ -136,280 +136,506 @@ struct SMTP { BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */ }; -/* Local API functions */ -static CURLcode smtp_regular_transfer(struct Curl_easy *data, - struct smtp_conn *smtpc, - struct SMTP *smtp, - bool *done); -static CURLcode smtp_do(struct Curl_easy *data, bool *done); -static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode smtp_connect(struct Curl_easy *data, bool *done); -static CURLcode smtp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode smtp_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode smtp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode smtp_parse_url_options(struct connectdata *conn, - struct smtp_conn *smtpc); -static CURLcode smtp_parse_url_path(struct Curl_easy *data, - struct smtp_conn *smtpc); -static CURLcode smtp_parse_custom_request(struct Curl_easy *data, - struct SMTP *smtp); -static CURLcode smtp_parse_address(const char *fqma, - char **address, struct hostname *host, - const char **suffix); -static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out); -static CURLcode cr_eob_add(struct Curl_easy *data); - -/* - * SMTP protocol handler. - */ - -const struct Curl_handler Curl_handler_smtp = { - "smtp", /* scheme */ - smtp_setup_connection, /* setup_connection */ - smtp_do, /* do_it */ - smtp_done, /* done */ - ZERO_NULL, /* do_more */ - smtp_connect, /* connect_it */ - smtp_multi_statemach, /* connecting */ - smtp_doing, /* doing */ - smtp_pollset, /* proto_pollset */ - smtp_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - smtp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SMTP, /* defport */ - CURLPROTO_SMTP, /* protocol */ - CURLPROTO_SMTP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE -}; - -#ifdef USE_SSL -/* - * SMTPS protocol handler. - */ - -const struct Curl_handler Curl_handler_smtps = { - "smtps", /* scheme */ - smtp_setup_connection, /* setup_connection */ - smtp_do, /* do_it */ - smtp_done, /* done */ - ZERO_NULL, /* do_more */ - smtp_connect, /* connect_it */ - smtp_multi_statemach, /* connecting */ - smtp_doing, /* doing */ - smtp_pollset, /* proto_pollset */ - smtp_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - smtp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SMTPS, /* defport */ - CURLPROTO_SMTPS, /* protocol */ - CURLPROTO_SMTP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE -}; -#endif - -/* SASL parameters for the smtp protocol */ -static const struct SASLproto saslsmtp = { - "smtp", /* The service name */ - smtp_perform_auth, /* Send authentication command */ - smtp_continue_auth, /* Send authentication continuation */ - smtp_cancel_auth, /* Cancel authentication */ - smtp_get_message, /* Get SASL response message */ - 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ - 334, /* Code received when continuation is expected */ - 235, /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ -}; - /*********************************************************************** * - * smtp_endofresp() + * smtp_parse_url_options() * - * Checks for an ending SMTP status code at the start of the given string, but - * also detects various capabilities from the EHLO response including the - * supported authentication mechanisms. + * Parse the URL login options. */ -static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, - const char *line, size_t len, int *resp) +static CURLcode smtp_parse_url_options(struct connectdata *conn, + struct smtp_conn *smtpc) { - struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); - bool result = FALSE; - (void)data; - - DEBUGASSERT(smtpc); - if(!smtpc) - return FALSE; - - /* Nothing for us */ - if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) - return FALSE; - - /* Do we have a command response? This should be the response code followed - by a space and optionally some text as per RFC-5321 and as outlined in - Section 4. Examples of RFC-4954 but some email servers ignore this and - only send the response code instead as per Section 4.2. */ - if(line[3] == ' ' || len == 5) { - char tmpline[6]; - curl_off_t code; - const char *p = tmpline; - result = TRUE; - memcpy(tmpline, line, (len == 5 ? 5 : 3)); - tmpline[len == 5 ? 5 : 3] = 0; - if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999)) - return FALSE; - *resp = (int)code; - - /* Make sure real server never sends internal value */ - if(*resp == 1) - *resp = 0; - } - /* Do we have a multiline (continuation) response? */ - else if(line[3] == '-' && - (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { - result = TRUE; - *resp = 1; /* Internal response code */ - } + CURLcode result = CURLE_OK; + const char *ptr = conn->options; - return result; -} + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; -/*********************************************************************** - * - * smtp_get_message() - * - * Gets the authentication message from the response buffer. - */ -static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) -{ - struct smtp_conn *smtpc = - Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); - char *message; - size_t len; + while(*ptr && *ptr != '=') + ptr++; - if(!smtpc) - return CURLE_FAILED_INIT; + value = ptr + 1; - message = curlx_dyn_ptr(&smtpc->pp.recvbuf); - len = smtpc->pp.nfinal; - if(len > 4) { - /* Find the start of the message */ - len -= 4; - for(message += 4; *message == ' ' || *message == '\t'; message++, len--) - ; + while(*ptr && *ptr != ';') + ptr++; - /* Find the end of the message */ - while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') - break; + if(curl_strnequal(key, "AUTH=", 5)) + result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, + value, ptr - value); + else + result = CURLE_URL_MALFORMAT; - /* Terminate the message */ - message[++len] = '\0'; - Curl_bufref_set(out, message, len, NULL); + if(*ptr == ';') + ptr++; } - else - /* junk input => zero length output */ - Curl_bufref_set(out, "", 0, NULL); - return CURLE_OK; + return result; } /*********************************************************************** * - * smtp_state() + * smtp_parse_url_path() * - * This is the ONLY way to change SMTP state! + * Parse the URL path into separate path components. */ -static void smtp_state(struct Curl_easy *data, - struct smtp_conn *smtpc, - smtpstate newstate) +static CURLcode smtp_parse_url_path(struct Curl_easy *data, + struct smtp_conn *smtpc) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - /* for debug purposes */ - static const char * const names[] = { - "STOP", - "SERVERGREET", - "EHLO", - "HELO", - "STARTTLS", - "UPGRADETLS", - "AUTH", - "COMMAND", - "MAIL", - "RCPT", - "DATA", - "POSTDATA", - "QUIT", - /* LAST */ - }; + /* The SMTP struct is already initialised in smtp_connect() */ + const char *path = &data->state.up.path[1]; /* skip leading path */ + char localhost[HOSTNAME_MAX + 1]; - if(smtpc->state != newstate) - CURL_TRC_SMTP(data, "state change from %s to %s", - names[smtpc->state], names[newstate]); -#else - (void)data; -#endif + /* Calculate the path if necessary */ + if(!*path) { + if(!Curl_gethostname(localhost, sizeof(localhost))) + path = localhost; + else + path = "localhost"; + } - smtpc->state = newstate; + /* URL decode the path and use it as the domain in our EHLO */ + return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL); } /*********************************************************************** * - * smtp_perform_ehlo() + * smtp_parse_custom_request() * - * Sends the EHLO command to not only initialise communication with the ESMTP - * server but to also obtain a list of server side supported capabilities. + * Parse the custom request. */ -static CURLcode smtp_perform_ehlo(struct Curl_easy *data, - struct smtp_conn *smtpc) +static CURLcode smtp_parse_custom_request(struct Curl_easy *data, + struct SMTP *smtp) { CURLcode result = CURLE_OK; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ - smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism - used for esmtp connections */ - smtpc->tls_supported = FALSE; /* Clear the TLS capability */ - smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ - - /* Send the EHLO command */ - result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain); - - if(!result) - smtp_state(data, smtpc, SMTP_EHLO); + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL); return result; } /*********************************************************************** * - * smtp_perform_helo() + * smtp_parse_address() * - * Sends the HELO command to initialise communication with the SMTP server. + * Parse the fully qualified mailbox address into a local address part and the + * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if + * necessary. + * + * Parameters: + * + * fqma [in] - The fully qualified mailbox address (which may or + * may not contain UTF-8 characters). + * address [in/out] - A new allocated buffer which holds the local + * address part of the mailbox. This buffer must be + * free'ed by the caller. + * host [in/out] - The hostname structure that holds the original, + * and optionally encoded, hostname. + * Curl_free_idnconverted_hostname() must be called + * once the caller has finished with the structure. + * + * Returns CURLE_OK on success. + * + * Notes: + * + * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor + * that conversion then we shall return success. This allow the caller to send + * the data to the server as a U-label (as per RFC-6531 sect. 3.2). + * + * If an mailbox '@' separator cannot be located then the mailbox is considered + * to be either a local mailbox or an invalid mailbox (depending on what the + * calling function deems it to be) then the input will be returned in + * the address part with the hostname being NULL. + */ +static CURLcode smtp_parse_address(const char *fqma, char **address, + struct hostname *host, const char **suffix) +{ + CURLcode result = CURLE_OK; + size_t length; + char *addressend; + + /* Duplicate the fully qualified email address so we can manipulate it, + ensuring it does not contain the delimiters if specified */ + char *dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma); + if(!dup) + return CURLE_OUT_OF_MEMORY; + + if(fqma[0] != '<') { + length = strlen(dup); + if(length) { + if(dup[length - 1] == '>') + dup[length - 1] = '\0'; + } + } + else { + addressend = strrchr(dup, '>'); + if(addressend) { + *addressend = '\0'; + *suffix = addressend + 1; + } + } + + /* Extract the hostname from the address (if we can) */ + host->name = strpbrk(dup, "@"); + if(host->name) { + *host->name = '\0'; + host->name = host->name + 1; + + /* Attempt to convert the hostname to IDN ACE */ + (void)Curl_idnconvert_hostname(host); + + /* If Curl_idnconvert_hostname() fails then we shall attempt to continue + and send the hostname using UTF-8 rather than as 7-bit ACE (which is + our preference) */ + } + + /* Extract the local address from the mailbox */ + *address = dup; + + return result; +} + +struct cr_eob_ctx { + struct Curl_creader super; + struct bufq buf; + size_t n_eob; /* how many EOB bytes we matched so far */ + size_t eob; /* Number of bytes of the EOB (End Of Body) that + have been received so far */ + BIT(read_eos); /* we read an EOS from the next reader */ + BIT(processed_eos); /* we read and processed an EOS */ + BIT(eos); /* we have returned an EOS */ +}; + +static CURLcode cr_eob_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_eob_ctx *ctx = reader->ctx; + (void)data; + /* The first char we read is the first on a line, as if we had + * read CRLF before */ + ctx->n_eob = 2; + Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT); + return CURLE_OK; +} + +static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader) +{ + struct cr_eob_ctx *ctx = reader->ctx; + (void)data; + Curl_bufq_free(&ctx->buf); +} + +/* this is the 5-bytes End-Of-Body marker for SMTP */ +#define SMTP_EOB "\r\n.\r\n" +#define SMTP_EOB_FIND_LEN 3 + +/* client reader doing SMTP End-Of-Body escaping. */ +static CURLcode cr_eob_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_eob_ctx *ctx = reader->ctx; + CURLcode result = CURLE_OK; + size_t nread, i, start, n; + bool eos; + + if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* Get more and convert it when needed */ + result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); + CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d", + blen, result, nread, eos); + if(result) + return result; + + ctx->read_eos = eos; + if(nread) { + if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) { + /* not in the middle of a match, no EOB start found, pass */ + *pnread = nread; + *peos = FALSE; + return CURLE_OK; + } + /* scan for EOB (continuation) and convert */ + for(i = start = 0; i < nread; ++i) { + if(ctx->n_eob >= SMTP_EOB_FIND_LEN) { + /* matched the EOB prefix and seeing additional char, add '.' */ + result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); + if(result) + return result; + result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n); + if(result) + return result; + ctx->n_eob = 0; + start = i; + if(data->state.infilesize > 0) + data->state.infilesize++; + } + + if(buf[i] != SMTP_EOB[ctx->n_eob]) + ctx->n_eob = 0; + + if(buf[i] == SMTP_EOB[ctx->n_eob]) { + /* matching another char of the EOB */ + ++ctx->n_eob; + } + } + + /* add any remainder to buf */ + if(start < nread) { + result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n); + if(result) + return result; + } + } + } + + *peos = FALSE; + + if(ctx->read_eos && !ctx->processed_eos) { + /* if we last matched a CRLF or if the data was empty, add ".\r\n" + * to end the body. If we sent something and it did not end with "\r\n", + * add "\r\n.\r\n" to end the body */ + const char *eob = SMTP_EOB; + CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'"); + switch(ctx->n_eob) { + case 2: + /* seen a CRLF at the end, add the remainder */ + eob = &SMTP_EOB[2]; + break; + case 3: + /* ended with '\r\n.', we should escape the last '.' */ + eob = "." SMTP_EOB; + break; + default: + break; + } + result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n); + if(result) + return result; + ctx->processed_eos = TRUE; + } + + if(!Curl_bufq_is_empty(&ctx->buf)) { + result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); + } + else + *pnread = 0; + + if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* no more data, read all, done. */ + CURL_TRC_SMTP(data, "mail body complete, returning EOS"); + ctx->eos = TRUE; + } + *peos = (bool)ctx->eos; + DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d", + blen, result, *pnread, *peos)); + return result; +} + +static curl_off_t cr_eob_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + /* this reader changes length depending on input */ + (void)data; + (void)reader; + return -1; +} + +static const struct Curl_crtype cr_eob = { + "cr-smtp-eob", + cr_eob_init, + cr_eob_read, + cr_eob_close, + Curl_creader_def_needs_rewind, + cr_eob_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_cntrl, + Curl_creader_def_is_paused, + Curl_creader_def_done, + sizeof(struct cr_eob_ctx) +}; + +static CURLcode cr_eob_add(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +/*********************************************************************** + * + * smtp_endofresp() + * + * Checks for an ending SMTP status code at the start of the given string, but + * also detects various capabilities from the EHLO response including the + * supported authentication mechanisms. + */ +static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, + const char *line, size_t len, int *resp) +{ + struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); + bool result = FALSE; + (void)data; + + DEBUGASSERT(smtpc); + if(!smtpc) + return FALSE; + + /* Nothing for us */ + if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) + return FALSE; + + /* Do we have a command response? This should be the response code followed + by a space and optionally some text as per RFC-5321 and as outlined in + Section 4. Examples of RFC-4954 but some email servers ignore this and + only send the response code instead as per Section 4.2. */ + if(line[3] == ' ' || len == 5) { + char tmpline[6]; + curl_off_t code; + const char *p = tmpline; + result = TRUE; + memcpy(tmpline, line, (len == 5 ? 5 : 3)); + tmpline[len == 5 ? 5 : 3] = 0; + if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999)) + return FALSE; + *resp = (int)code; + + /* Make sure real server never sends internal value */ + if(*resp == 1) + *resp = 0; + } + /* Do we have a multiline (continuation) response? */ + else if(line[3] == '-' && + (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { + result = TRUE; + *resp = 1; /* Internal response code */ + } + + return result; +} + +/*********************************************************************** + * + * smtp_get_message() + * + * Gets the authentication message from the response buffer. + */ +static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) +{ + struct smtp_conn *smtpc = + Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); + char *message; + size_t len; + + if(!smtpc) + return CURLE_FAILED_INIT; + + message = curlx_dyn_ptr(&smtpc->pp.recvbuf); + len = smtpc->pp.nfinal; + if(len > 4) { + /* Find the start of the message */ + len -= 4; + for(message += 4; ISBLANK(*message); message++, len--) + ; + + /* Find the end of the message */ + while(len--) + if(!ISNEWLINE(message[len]) && !ISBLANK(message[len])) + break; + + /* Terminate the message */ + message[++len] = '\0'; + Curl_bufref_set(out, message, len, NULL); + } + else + /* junk input => zero length output */ + Curl_bufref_set(out, "", 0, NULL); + + return CURLE_OK; +} + +/*********************************************************************** + * + * smtp_state() + * + * This is the ONLY way to change SMTP state! + */ +static void smtp_state(struct Curl_easy *data, + struct smtp_conn *smtpc, + smtpstate newstate) +{ +#ifdef CURLVERBOSE + /* for debug purposes */ + static const char * const names[] = { + "STOP", + "SERVERGREET", + "EHLO", + "HELO", + "STARTTLS", + "UPGRADETLS", + "AUTH", + "COMMAND", + "MAIL", + "RCPT", + "DATA", + "POSTDATA", + "QUIT", + /* LAST */ + }; + + if(smtpc->state != newstate) + CURL_TRC_SMTP(data, "state change from %s to %s", + names[smtpc->state], names[newstate]); +#else + (void)data; +#endif + + smtpc->state = newstate; +} + +/*********************************************************************** + * + * smtp_perform_ehlo() + * + * Sends the EHLO command to not only initialise communication with the ESMTP + * server but to also obtain a list of server side supported capabilities. + */ +static CURLcode smtp_perform_ehlo(struct Curl_easy *data, + struct smtp_conn *smtpc) +{ + CURLcode result = CURLE_OK; + + smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ + smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism + used for esmtp connections */ + smtpc->tls_supported = FALSE; /* Clear the TLS capability */ + smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ + + /* Send the EHLO command */ + result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain); + + if(!result) + smtp_state(data, smtpc, SMTP_EHLO); + + return result; +} + +/*********************************************************************** + * + * smtp_perform_helo() + * + * Sends the HELO command to initialise communication with the SMTP server. */ static CURLcode smtp_perform_helo(struct Curl_easy *data, struct smtp_conn *smtpc) @@ -467,13 +693,13 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data, if(result) goto out; /* Change the connection handler and SMTP state */ - conn->handler = &Curl_handler_smtps; + conn->scheme = &Curl_scheme_smtps; } DEBUGASSERT(!smtpc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + result, ssldone)); if(!result && ssldone) { smtpc->ssldone = ssldone; /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */ @@ -608,7 +834,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data, whether the hostname is encoded using IDN ACE */ bool utf8 = FALSE; - if((!smtp->custom) || (!smtp->custom[0])) { + if(!smtp->custom || !smtp->custom[0]) { char *address = NULL; struct hostname host = { NULL, NULL, NULL, NULL }; const char *suffix = ""; @@ -622,9 +848,10 @@ static CURLcode smtp_perform_command(struct Curl_easy *data, /* Establish whether we should report SMTPUTF8 to the server for this mailbox as per RFC-6531 sect. 3.1 point 6 */ - utf8 = (smtpc->utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name))); + utf8 = smtpc->utf8_supported && + (host.encalloc || + !Curl_is_ASCII_name(address) || + !Curl_is_ASCII_name(host.name)); /* Send the VRFY command (Note: The hostname part may be absent when the host is a local system) */ @@ -697,9 +924,10 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, /* Establish whether we should report SMTPUTF8 to the server for this mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ - utf8 = (smtpc->utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name))); + utf8 = smtpc->utf8_supported && + (host.encalloc || + !Curl_is_ASCII_name(address) || + !Curl_is_ASCII_name(host.name)); if(host.name) { from = curl_maprintf("<%s@%s>%s", address, host.name, suffix); @@ -707,8 +935,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, Curl_free_idnconverted_hostname(&host); } else - /* An invalid mailbox was provided but we will simply let the server - worry about that and reply with a 501 error */ + /* An invalid mailbox was provided but we let the server worry + about that and reply with a 501 error */ from = curl_maprintf("<%s>%s", address, suffix); curlx_free(address); @@ -738,9 +966,10 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, /* Establish whether we should report SMTPUTF8 to the server for this mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ - if((!utf8) && (smtpc->utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name)))) + if(!utf8 && smtpc->utf8_supported && + (host.encalloc || + !Curl_is_ASCII_name(address) || + !Curl_is_ASCII_name(host.name))) utf8 = TRUE; if(host.name) { @@ -749,8 +978,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, Curl_free_idnconverted_hostname(&host); } else - /* An invalid mailbox was provided but we will simply let the server - worry about it */ + /* An invalid mailbox was provided but we let the server worry + about it */ auth = curl_maprintf("<%s>%s", address, suffix); curlx_free(address); } @@ -766,22 +995,24 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, #ifndef CURL_DISABLE_MIME /* Prepare the mime data if some. */ - if(data->set.mimepost.kind != MIMEKIND_NONE) { + if(IS_MIME_POST(data)) { + curl_mimepart *postp = data->set.mimepostp; + /* Use the whole structure as data. */ - data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; + postp->flags &= ~(unsigned int)MIME_BODY_ONLY; /* Add external headers and mime version. */ - curl_mime_headers(&data->set.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, + curl_mime_headers(postp, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, postp, NULL, NULL, MIMESTRATEGY_MAIL); if(!result) if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) - result = Curl_mime_add_header(&data->set.mimepost.curlheaders, + result = Curl_mime_add_header(&postp->curlheaders, "Mime-Version: 1.0"); if(!result) - result = Curl_creader_set_mime(data, &data->set.mimepost); + result = Curl_creader_set_mime(data, postp); if(result) goto out; data->state.infilesize = Curl_creader_total_length(data); @@ -830,9 +1061,9 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, "MAIL FROM:%s%s%s%s%s%s", from, /* Mandatory */ auth ? " AUTH=" : "", /* Optional on AUTH support */ - auth ? auth : "", /* */ + auth ? auth : "", size ? " SIZE=" : "", /* Optional on SIZE support */ - size ? size : "", /* */ + size ? size : "", utf8 ? " SMTPUTF8" /* Internationalised mailbox */ : ""); /* included in our envelope */ @@ -875,8 +1106,8 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data, result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s@%s>%s", address, host.name, suffix); else - /* An invalid mailbox was provided but we will simply let the server worry - about that and reply with a 501 error */ + /* An invalid mailbox was provided but we let the server worry about + that and reply with a 501 error */ result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s>%s", address, suffix); @@ -1004,10 +1235,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, size_t wordlen; unsigned short mechbit; - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - + while(len && (ISBLANK(*line) || ISNEWLINE(*line))) { line++; len--; } @@ -1016,9 +1244,8 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, break; /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) + for(wordlen = 0; wordlen < len && !ISBLANK(line[wordlen]) && + !ISNEWLINE(line[wordlen]);) wordlen++; /* Test the word for a matching authentication mechanism */ @@ -1113,7 +1340,7 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, smtpstate instate) { CURLcode result = CURLE_OK; - char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf); + const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf); size_t len = smtpc->pp.nfinal; (void)instate; @@ -1411,6 +1638,20 @@ static CURLcode smtp_pollset(struct Curl_easy *data, return smtpc ? Curl_pp_pollset(data, &smtpc->pp, ps) : CURLE_OK; } +/* SASL parameters for the smtp protocol */ +static const struct SASLproto saslsmtp = { + "smtp", /* The service name */ + smtp_perform_auth, /* Send authentication command */ + smtp_continue_auth, /* Send authentication continuation */ + smtp_cancel_auth, /* Cancel authentication */ + smtp_get_message, /* Get SASL response message */ + 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + 334, /* Code received when continuation is expected */ + 235, /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ +}; + /*********************************************************************** * * smtp_connect() @@ -1566,69 +1807,6 @@ static CURLcode smtp_perform(struct Curl_easy *data, return result; } -/*********************************************************************** - * - * smtp_do() - * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (smtp_perform). - * - * The input argument is already checked for validity. - */ -static CURLcode smtp_do(struct Curl_easy *data, bool *done) -{ - struct smtp_conn *smtpc = - Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); - struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY); - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - *done = FALSE; /* default to false */ - if(!smtpc || !smtp) - return CURLE_FAILED_INIT; - - /* Parse the custom request */ - result = smtp_parse_custom_request(data, smtp); - if(result) - return result; - - result = smtp_regular_transfer(data, smtpc, smtp, done); - CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done); - return result; -} - -/*********************************************************************** - * - * smtp_disconnect() - * - * Disconnect from an SMTP server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode smtp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); - - (void)data; - if(!smtpc) - return CURLE_FAILED_INIT; - - /* We cannot send quit unconditionally. If this connection is stale or - bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to. */ - - if(!dead_connection && conn->bits.protoconnstart && - !Curl_pp_needs_flush(data, &smtpc->pp)) { - if(!smtp_perform_quit(data, smtpc)) - (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */ - } - - CURL_TRC_SMTP(data, "smtp_disconnect(), finished"); - return CURLE_OK; -} - /* Call this when the DO phase has completed */ static CURLcode smtp_dophase_done(struct Curl_easy *data, struct SMTP *smtp, @@ -1643,27 +1821,6 @@ static CURLcode smtp_dophase_done(struct Curl_easy *data, return CURLE_OK; } -/* Called from multi.c while DOing */ -static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) -{ - struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY); - CURLcode result; - - if(!smtp) - return CURLE_FAILED_INIT; - result = smtp_multi_statemach(data, dophase_done); - if(result) - DEBUGF(infof(data, "DO phase failed")); - else if(*dophase_done) { - result = smtp_dophase_done(data, smtp, FALSE /* not connected */); - - DEBUGF(infof(data, "DO phase is complete")); - } - - CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done); - return result; -} - /*********************************************************************** * * smtp_regular_transfer() @@ -1699,384 +1856,186 @@ static CURLcode smtp_regular_transfer(struct Curl_easy *data, return result; } -static void smtp_easy_dtor(void *key, size_t klen, void *entry) -{ - struct SMTP *smtp = entry; - (void)key; - (void)klen; - curlx_free(smtp); -} - -static void smtp_conn_dtor(void *key, size_t klen, void *entry) -{ - struct smtp_conn *smtpc = entry; - (void)key; - (void)klen; - Curl_pp_disconnect(&smtpc->pp); - Curl_safefree(smtpc->domain); - curlx_free(smtpc); -} - -static CURLcode smtp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct smtp_conn *smtpc; - struct SMTP *smtp; - CURLcode result = CURLE_OK; - - smtpc = curlx_calloc(1, sizeof(*smtpc)); - if(!smtpc || - Curl_conn_meta_set(conn, CURL_META_SMTP_CONN, smtpc, smtp_conn_dtor)) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - smtp = curlx_calloc(1, sizeof(*smtp)); - if(!smtp || - Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor)) - result = CURLE_OUT_OF_MEMORY; - -out: - CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result); - return result; -} - -/*********************************************************************** - * - * smtp_parse_url_options() - * - * Parse the URL login options. - */ -static CURLcode smtp_parse_url_options(struct connectdata *conn, - struct smtp_conn *smtpc) -{ - CURLcode result = CURLE_OK; - const char *ptr = conn->options; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(curl_strnequal(key, "AUTH=", 5)) - result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, - value, ptr - value); - else - result = CURLE_URL_MALFORMAT; - - if(*ptr == ';') - ptr++; - } - - return result; -} - -/*********************************************************************** - * - * smtp_parse_url_path() - * - * Parse the URL path into separate path components. - */ -static CURLcode smtp_parse_url_path(struct Curl_easy *data, - struct smtp_conn *smtpc) -{ - /* The SMTP struct is already initialised in smtp_connect() */ - const char *path = &data->state.up.path[1]; /* skip leading path */ - char localhost[HOSTNAME_MAX + 1]; - - /* Calculate the path if necessary */ - if(!*path) { - if(!Curl_gethostname(localhost, sizeof(localhost))) - path = localhost; - else - path = "localhost"; - } - - /* URL decode the path and use it as the domain in our EHLO */ - return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL); -} - -/*********************************************************************** - * - * smtp_parse_custom_request() - * - * Parse the custom request. - */ -static CURLcode smtp_parse_custom_request(struct Curl_easy *data, - struct SMTP *smtp) -{ - CURLcode result = CURLE_OK; - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - - /* URL decode the custom request */ - if(custom) - result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL); - - return result; -} - -/*********************************************************************** - * - * smtp_parse_address() - * - * Parse the fully qualified mailbox address into a local address part and the - * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if - * necessary. - * - * Parameters: - * - * conn [in] - The connection handle. - * fqma [in] - The fully qualified mailbox address (which may or - * may not contain UTF-8 characters). - * address [in/out] - A new allocated buffer which holds the local - * address part of the mailbox. This buffer must be - * free'ed by the caller. - * host [in/out] - The hostname structure that holds the original, - * and optionally encoded, hostname. - * Curl_free_idnconverted_hostname() must be called - * once the caller has finished with the structure. - * - * Returns CURLE_OK on success. - * - * Notes: - * - * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor - * that conversion then we shall return success. This allow the caller to send - * the data to the server as a U-label (as per RFC-6531 sect. 3.2). - * - * If an mailbox '@' separator cannot be located then the mailbox is considered - * to be either a local mailbox or an invalid mailbox (depending on what the - * calling function deems it to be) then the input will simply be returned in - * the address part with the hostname being NULL. - */ -static CURLcode smtp_parse_address(const char *fqma, char **address, - struct hostname *host, const char **suffix) -{ - CURLcode result = CURLE_OK; - size_t length; - char *addressend; - - /* Duplicate the fully qualified email address so we can manipulate it, - ensuring it does not contain the delimiters if specified */ - char *dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma); - if(!dup) - return CURLE_OUT_OF_MEMORY; - - if(fqma[0] != '<') { - length = strlen(dup); - if(length) { - if(dup[length - 1] == '>') - dup[length - 1] = '\0'; - } - } - else { - addressend = strrchr(dup, '>'); - if(addressend) { - *addressend = '\0'; - *suffix = addressend + 1; - } - } - - /* Extract the hostname from the address (if we can) */ - host->name = strpbrk(dup, "@"); - if(host->name) { - *host->name = '\0'; - host->name = host->name + 1; - - /* Attempt to convert the hostname to IDN ACE */ - (void)Curl_idnconvert_hostname(host); - - /* If Curl_idnconvert_hostname() fails then we shall attempt to continue - and send the hostname using UTF-8 rather than as 7-bit ACE (which is - our preference) */ - } - - /* Extract the local address from the mailbox */ - *address = dup; - - return result; -} - -struct cr_eob_ctx { - struct Curl_creader super; - struct bufq buf; - size_t n_eob; /* how many EOB bytes we matched so far */ - size_t eob; /* Number of bytes of the EOB (End Of Body) that - have been received so far */ - BIT(read_eos); /* we read an EOS from the next reader */ - BIT(processed_eos); /* we read and processed an EOS */ - BIT(eos); /* we have returned an EOS */ -}; - -static CURLcode cr_eob_init(struct Curl_easy *data, - struct Curl_creader *reader) -{ - struct cr_eob_ctx *ctx = reader->ctx; - (void)data; - /* The first char we read is the first on a line, as if we had - * read CRLF just before */ - ctx->n_eob = 2; - Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT); - return CURLE_OK; -} - -static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader) -{ - struct cr_eob_ctx *ctx = reader->ctx; - (void)data; - Curl_bufq_free(&ctx->buf); -} - -/* this is the 5-bytes End-Of-Body marker for SMTP */ -#define SMTP_EOB "\r\n.\r\n" -#define SMTP_EOB_FIND_LEN 3 - -/* client reader doing SMTP End-Of-Body escaping. */ -static CURLcode cr_eob_read(struct Curl_easy *data, - struct Curl_creader *reader, - char *buf, size_t blen, - size_t *pnread, bool *peos) +/*********************************************************************** + * + * smtp_do() + * + * This function is registered as 'curl_do' function. It decodes the path + * parts etc as a wrapper to the actual DO function (smtp_perform). + * + * The input argument is already checked for validity. + */ +static CURLcode smtp_do(struct Curl_easy *data, bool *done) { - struct cr_eob_ctx *ctx = reader->ctx; + struct smtp_conn *smtpc = + Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); + struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY); CURLcode result = CURLE_OK; - size_t nread, i, start, n; - bool eos; - if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { - /* Get more and convert it when needed */ - result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); - CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d", - blen, result, nread, eos); - if(result) - return result; + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + *done = FALSE; /* default to false */ + if(!smtpc || !smtp) + return CURLE_FAILED_INIT; - ctx->read_eos = eos; - if(nread) { - if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) { - /* not in the middle of a match, no EOB start found, just pass */ - *pnread = nread; - *peos = FALSE; - return CURLE_OK; - } - /* scan for EOB (continuation) and convert */ - for(i = start = 0; i < nread; ++i) { - if(ctx->n_eob >= SMTP_EOB_FIND_LEN) { - /* matched the EOB prefix and seeing additional char, add '.' */ - result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); - if(result) - return result; - result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n); - if(result) - return result; - ctx->n_eob = 0; - start = i; - if(data->state.infilesize > 0) - data->state.infilesize++; - } + /* Parse the custom request */ + result = smtp_parse_custom_request(data, smtp); + if(result) + return result; - if(buf[i] != SMTP_EOB[ctx->n_eob]) - ctx->n_eob = 0; + result = smtp_regular_transfer(data, smtpc, smtp, done); + CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done); + return result; +} - if(buf[i] == SMTP_EOB[ctx->n_eob]) { - /* matching another char of the EOB */ - ++ctx->n_eob; - } - } +/*********************************************************************** + * + * smtp_disconnect() + * + * Disconnect from an SMTP server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode smtp_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); - /* add any remainder to buf */ - if(start < nread) { - result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n); - if(result) - return result; - } - } - } + if(!smtpc) + return CURLE_FAILED_INIT; - *peos = FALSE; + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way, sending quit and waiting around here will make the + disconnect wait in vain and cause more problems than we need to. */ - if(ctx->read_eos && !ctx->processed_eos) { - /* if we last matched a CRLF or if the data was empty, add ".\r\n" - * to end the body. If we sent something and it did not end with "\r\n", - * add "\r\n.\r\n" to end the body */ - const char *eob = SMTP_EOB; - CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'"); - switch(ctx->n_eob) { - case 2: - /* seen a CRLF at the end, just add the remainder */ - eob = &SMTP_EOB[2]; - break; - case 3: - /* ended with '\r\n.', we should escape the last '.' */ - eob = "." SMTP_EOB; - break; - default: - break; - } - result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n); - if(result) - return result; - ctx->processed_eos = TRUE; + if(!dead_connection && conn->bits.protoconnstart && + !Curl_pp_needs_flush(data, &smtpc->pp)) { + if(!smtp_perform_quit(data, smtpc)) + (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */ } - if(!Curl_bufq_is_empty(&ctx->buf)) { - result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); - } - else - *pnread = 0; + CURL_TRC_SMTP(data, "smtp_disconnect(), finished"); + return CURLE_OK; +} - if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { - /* no more data, read all, done. */ - CURL_TRC_SMTP(data, "mail body complete, returning EOS"); - ctx->eos = TRUE; +/* Called from multi.c while DOing */ +static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) +{ + struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY); + CURLcode result; + + if(!smtp) + return CURLE_FAILED_INIT; + result = smtp_multi_statemach(data, dophase_done); + if(result) + DEBUGF(infof(data, "DO phase failed")); + else if(*dophase_done) { + result = smtp_dophase_done(data, smtp, FALSE /* not connected */); + + DEBUGF(infof(data, "DO phase is complete")); } - *peos = ctx->eos; - DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d", - blen, result, *pnread, *peos)); + + CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done); return result; } -static curl_off_t cr_eob_total_length(struct Curl_easy *data, - struct Curl_creader *reader) +static void smtp_easy_dtor(void *key, size_t klen, void *entry) { - /* this reader changes length depending on input */ - (void)data; - (void)reader; - return -1; + struct SMTP *smtp = entry; + (void)key; + (void)klen; + curlx_free(smtp); } -static const struct Curl_crtype cr_eob = { - "cr-smtp-eob", - cr_eob_init, - cr_eob_read, - cr_eob_close, - Curl_creader_def_needs_rewind, - cr_eob_total_length, - Curl_creader_def_resume_from, - Curl_creader_def_cntrl, - Curl_creader_def_is_paused, - Curl_creader_def_done, - sizeof(struct cr_eob_ctx) -}; +static void smtp_conn_dtor(void *key, size_t klen, void *entry) +{ + struct smtp_conn *smtpc = entry; + (void)key; + (void)klen; + Curl_pp_disconnect(&smtpc->pp); + Curl_safefree(smtpc->domain); + curlx_free(smtpc); +} -static CURLcode cr_eob_add(struct Curl_easy *data) +static CURLcode smtp_setup_connection(struct Curl_easy *data, + struct connectdata *conn) { - struct Curl_creader *reader = NULL; - CURLcode result; + struct smtp_conn *smtpc; + struct SMTP *smtp; + CURLcode result = CURLE_OK; - result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE); - if(!result) - result = Curl_creader_add(data, reader); + smtpc = curlx_calloc(1, sizeof(*smtpc)); + if(!smtpc || + Curl_conn_meta_set(conn, CURL_META_SMTP_CONN, smtpc, smtp_conn_dtor)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } - if(result && reader) - Curl_creader_free(data, reader); + smtp = curlx_calloc(1, sizeof(*smtp)); + if(!smtp || + Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor)) + result = CURLE_OUT_OF_MEMORY; + +out: + CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result); return result; } +/* + * SMTP protocol handler. + */ +static const struct Curl_protocol Curl_protocol_smtp = { + smtp_setup_connection, /* setup_connection */ + smtp_do, /* do_it */ + smtp_done, /* done */ + ZERO_NULL, /* do_more */ + smtp_connect, /* connect_it */ + smtp_multi_statemach, /* connecting */ + smtp_doing, /* doing */ + smtp_pollset, /* proto_pollset */ + smtp_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + smtp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_SMTP */ + +/* + * SMTP protocol handler. + */ +const struct Curl_scheme Curl_scheme_smtp = { + "smtp", /* scheme */ +#ifdef CURL_DISABLE_SMTP + ZERO_NULL, +#else + &Curl_protocol_smtp, +#endif + CURLPROTO_SMTP, /* protocol */ + CURLPROTO_SMTP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE, + PORT_SMTP, /* defport */ +}; + +/* + * SMTPS protocol handler. + */ +const struct Curl_scheme Curl_scheme_smtps = { + "smtps", /* scheme */ +#if defined(CURL_DISABLE_SMTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_smtp, +#endif + CURLPROTO_SMTPS, /* protocol */ + CURLPROTO_SMTP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_SMTPS, /* defport */ +}; diff --git a/vendor/curl/lib/smtp.h b/vendor/curl/lib/smtp.h index 7e3cf77..39f8d78 100644 --- a/vendor/curl/lib/smtp.h +++ b/vendor/curl/lib/smtp.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_handler Curl_handler_smtp; -extern const struct Curl_handler Curl_handler_smtps; +extern const struct Curl_scheme Curl_scheme_smtp; +extern const struct Curl_scheme Curl_scheme_smtps; #endif /* HEADER_CURL_SMTP_H */ diff --git a/vendor/curl/lib/socketpair.c b/vendor/curl/lib/socketpair.c index bdbe956..76b959d 100644 --- a/vendor/curl/lib/socketpair.c +++ b/vendor/curl/lib/socketpair.c @@ -28,11 +28,14 @@ #include "rand.h" #include "curlx/nonblock.h" +#ifndef CURL_DISABLE_SOCKETPAIR + +/* choose implementation */ #ifdef USE_EVENTFD #include -int Curl_eventfd(curl_socket_t socks[2], bool nonblocking) +static int wakeup_eventfd(curl_socket_t socks[2], bool nonblocking) { int efd = eventfd(0, nonblocking ? EFD_CLOEXEC | EFD_NONBLOCK : EFD_CLOEXEC); if(efd == -1) { @@ -49,7 +52,7 @@ int Curl_eventfd(curl_socket_t socks[2], bool nonblocking) #include #endif -int Curl_pipe(curl_socket_t socks[2], bool nonblocking) +static int wakeup_pipe(curl_socket_t socks[2], bool nonblocking) { #ifdef HAVE_PIPE2 int flags = nonblocking ? O_NONBLOCK | O_CLOEXEC : O_CLOEXEC; @@ -61,8 +64,8 @@ int Curl_pipe(curl_socket_t socks[2], bool nonblocking) #ifdef HAVE_FCNTL if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || fcntl(socks[1], F_SETFD, FD_CLOEXEC)) { - close(socks[0]); - close(socks[1]); + sclose(socks[0]); + sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } @@ -70,8 +73,8 @@ int Curl_pipe(curl_socket_t socks[2], bool nonblocking) if(nonblocking) { if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) { - close(socks[0]); - close(socks[1]); + sclose(socks[0]); + sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } @@ -81,33 +84,49 @@ int Curl_pipe(curl_socket_t socks[2], bool nonblocking) return 0; } -#endif /* USE_EVENTFD */ +#elif defined(HAVE_SOCKETPAIR) /* !USE_EVENTFD && !HAVE_PIPE */ -#ifndef CURL_DISABLE_SOCKETPAIR -#ifdef HAVE_SOCKETPAIR -#ifdef USE_SOCKETPAIR -int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2], bool nonblocking) +#ifndef USE_UNIX_SOCKETS +#error "unsupported Unix domain and socketpair build combo" +#endif + +static int wakeup_socketpair(curl_socket_t socks[2], bool nonblocking) { + int type = SOCK_STREAM; +#ifdef SOCK_CLOEXEC + type |= SOCK_CLOEXEC; +#endif #ifdef SOCK_NONBLOCK - type = nonblocking ? type | SOCK_NONBLOCK : type; + if(nonblocking) + type |= SOCK_NONBLOCK; #endif - if(CURL_SOCKETPAIR(domain, type, protocol, socks)) + + if(CURL_SOCKETPAIR(AF_UNIX, type, 0, socks)) return -1; #ifndef SOCK_NONBLOCK if(nonblocking) { if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) { - close(socks[0]); - close(socks[1]); + sclose(socks[0]); + sclose(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } } #endif +#ifdef USE_SO_NOSIGPIPE + if(Curl_sock_nosigpipe(socks[1]) < 0) { + sclose(socks[0]); + sclose(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; + return -1; + } +#endif /* USE_SO_NOSIGPIPE */ + return 0; } -#endif /* USE_SOCKETPAIR */ -#else /* !HAVE_SOCKETPAIR */ + +#else /* !USE_EVENTFD && !HAVE_PIPE && !HAVE_SOCKETPAIR */ #ifdef HAVE_NETDB_H #include @@ -125,8 +144,7 @@ int Curl_socketpair(int domain, int type, int protocol, #include "select.h" /* for Curl_poll */ -int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2], bool nonblocking) +static int wakeup_inet(curl_socket_t socks[2], bool nonblocking) { union { struct sockaddr_in inaddr; @@ -136,9 +154,6 @@ int Curl_socketpair(int domain, int type, int protocol, curl_socklen_t addrlen = sizeof(a.inaddr); int reuse = 1; struct pollfd pfd[1]; - (void)domain; - (void)type; - (void)protocol; listener = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(listener == CURL_SOCKET_BAD) @@ -248,6 +263,10 @@ int Curl_socketpair(int domain, int type, int protocol, if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) goto error; +#ifdef USE_SO_NOSIGPIPE + if(Curl_sock_nosigpipe(socks[1]) < 0) + goto error; +#endif sclose(listener); return 0; @@ -255,7 +274,103 @@ int Curl_socketpair(int domain, int type, int protocol, sclose(listener); sclose(socks[0]); sclose(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } + +#endif /* choose implementation */ + +int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking) +{ +#ifdef USE_EVENTFD + return wakeup_eventfd(socks, nonblocking); +#elif defined(HAVE_PIPE) + return wakeup_pipe(socks, nonblocking); +#elif defined(HAVE_SOCKETPAIR) + return wakeup_socketpair(socks, nonblocking); +#else + return wakeup_inet(socks, nonblocking); +#endif +} + +#if defined(USE_EVENTFD) || defined(HAVE_PIPE) + +#define wakeup_write write +#define wakeup_read read +#define wakeup_close close + +#else /* !USE_EVENTFD && !HAVE_PIPE */ + +#define wakeup_write swrite +#define wakeup_read sread +#define wakeup_close sclose + +#endif + +int Curl_wakeup_signal(curl_socket_t socks[2]) +{ + int err = 0; +#ifdef USE_EVENTFD + const uint64_t buf[1] = { 1 }; +#else + const char buf[1] = { 1 }; +#endif + + while(1) { + err = 0; + if(wakeup_write(socks[1], buf, sizeof(buf)) < 0) { + err = SOCKERRNO; +#ifdef USE_WINSOCK + if(err == SOCKEWOULDBLOCK) + err = 0; /* wakeup is already ongoing */ +#else + if(SOCKEINTR == err) + continue; + if((err == SOCKEWOULDBLOCK) || (err == EAGAIN)) + err = 0; /* wakeup is already ongoing */ +#endif + } + break; + } + return err; +} + +CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) +{ + char buf[64]; + ssize_t rc; + CURLcode result = CURLE_OK; + + do { + rc = wakeup_read(socks[0], buf, sizeof(buf)); + if(!rc) + break; + else if(rc < 0) { +#ifdef USE_WINSOCK + if(SOCKERRNO == SOCKEWOULDBLOCK) + break; +#else + if(SOCKEINTR == SOCKERRNO) + continue; + if((SOCKERRNO == SOCKEWOULDBLOCK) || (SOCKERRNO == EAGAIN)) + break; +#endif + result = CURLE_READ_ERROR; + break; + } + } while(all); + return result; +} + +void Curl_wakeup_destroy(curl_socket_t socks[2]) +{ +#ifndef USE_EVENTFD + if(socks[1] != CURL_SOCKET_BAD) + wakeup_close(socks[1]); #endif + if(socks[0] != CURL_SOCKET_BAD) + wakeup_close(socks[0]); + socks[0] = socks[1] = CURL_SOCKET_BAD; +} + #endif /* !CURL_DISABLE_SOCKETPAIR */ diff --git a/vendor/curl/lib/socketpair.h b/vendor/curl/lib/socketpair.h index 36685e7..2aa1733 100644 --- a/vendor/curl/lib/socketpair.h +++ b/vendor/curl/lib/socketpair.h @@ -25,53 +25,19 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef USE_EVENTFD - -#define wakeup_write write -#define wakeup_read read -#define wakeup_close close -#define wakeup_create(p, nb) Curl_eventfd(p, nb) - -int Curl_eventfd(curl_socket_t socks[2], bool nonblocking); - -#elif defined(HAVE_PIPE) - -#define wakeup_write write -#define wakeup_read read -#define wakeup_close close -#define wakeup_create(p, nb) Curl_pipe(p, nb) +#ifndef CURL_DISABLE_SOCKETPAIR -int Curl_pipe(curl_socket_t socks[2], bool nonblocking); +/* return < 0 for failure to initialise */ +int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking); +void Curl_wakeup_destroy(curl_socket_t socks[2]); -#else /* !USE_EVENTFD && !HAVE_PIPE */ +/* return 0 on success or errno on failure */ +int Curl_wakeup_signal(curl_socket_t socks[2]); -#define wakeup_write swrite -#define wakeup_read sread -#define wakeup_close sclose +CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all); -#if defined(USE_UNIX_SOCKETS) && defined(HAVE_SOCKETPAIR) -#define SOCKETPAIR_FAMILY AF_UNIX -#elif !defined(HAVE_SOCKETPAIR) -#define SOCKETPAIR_FAMILY 0 /* not used */ #else -#error "unsupported Unix domain and socketpair build combo" -#endif - -#ifdef SOCK_CLOEXEC -#define SOCKETPAIR_TYPE (SOCK_STREAM | SOCK_CLOEXEC) -#else -#define SOCKETPAIR_TYPE SOCK_STREAM -#endif - -#define USE_SOCKETPAIR -#define wakeup_create(p, nb) \ - Curl_socketpair(SOCKETPAIR_FAMILY, SOCKETPAIR_TYPE, 0, p, nb) - -#endif /* USE_EVENTFD */ - -#ifndef CURL_DISABLE_SOCKETPAIR -int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2], bool nonblocking); +#define Curl_wakeup_destroy(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_SOCKETPAIR_H */ diff --git a/vendor/curl/lib/socks.c b/vendor/curl/lib/socks.c index e49e8c8..b7c3bd6 100644 --- a/vendor/curl/lib/socks.c +++ b/vendor/curl/lib/socks.c @@ -34,17 +34,13 @@ #include "urldata.h" #include "bufq.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "select.h" #include "cfilters.h" #include "connect.h" #include "socks.h" #include "curlx/inet_pton.h" -#include "url.h" - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) -#define DEBUG_AND_VERBOSE -#endif /* for the (SOCKS) connect state machine */ enum socks_state_t { @@ -71,7 +67,7 @@ enum socks_state_t { SOCKS_ST_FAILED }; -#ifdef DEBUG_AND_VERBOSE +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) static const char * const cf_socks_statename[] = { "SOCKS_INIT", "SOCKS4_START", @@ -130,7 +126,7 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf, *pnread = 0; for(;;) { - timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE); + timediff_t timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { /* we already got the timeout */ return CURLE_OPERATION_TIMEDOUT; @@ -140,7 +136,7 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf, if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) return CURLE_OPERATION_TIMEDOUT; result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) continue; else if(result) return result; @@ -159,8 +155,7 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf, } #endif -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) -#define DEBUG_AND_VERBOSE +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) #define sxstate(x, c, d, y) socksstate(x, c, d, y, __LINE__) #else #define sxstate(x, c, d, y) socksstate(x, c, d, y) @@ -171,24 +166,26 @@ static void socksstate(struct socks_state *sx, struct Curl_cfilter *cf, struct Curl_easy *data, enum socks_state_t state -#ifdef DEBUG_AND_VERBOSE +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) , int lineno #endif ) { enum socks_state_t oldstate = sx->state; - (void)cf; - (void)data; + if(oldstate == state) /* do not bother when the new state is the same as the old state */ return; sx->state = state; -#ifdef DEBUG_AND_VERBOSE +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) CURL_TRC_CF(data, cf, "[%s] -> [%s] (line %d)", cf_socks_statename[oldstate], cf_socks_statename[sx->state], lineno); +#else + (void)cf; + (void)data; #endif } @@ -270,8 +267,8 @@ static CURLproxycode socks4_req_add_hd(struct socks_state *sx, (void)data; buf[0] = 4; /* version (SOCKS4) */ buf[1] = 1; /* connect */ - buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffu); /* MSB */ - buf[3] = (unsigned char)(sx->remote_port & 0xffu); /* LSB */ + buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffU); /* MSB */ + buf[3] = (unsigned char)(sx->remote_port & 0xffU); /* LSB */ result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten); if(result || (nwritten != 4)) @@ -344,6 +341,8 @@ static CURLproxycode socks4_resolving(struct socks_state *sx, if(result || !dns) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", sx->hostname); + if(dns) + Curl_resolv_unlink(data, &dns); return CURLPX_RESOLVE_HOST; } @@ -374,6 +373,7 @@ static CURLproxycode socks4_resolving(struct socks_state *sx, return CURLPX_SEND_REQUEST; } else { + Curl_resolv_unlink(data, &dns); failf(data, "SOCKS4 connection to %s not supported", sx->hostname); return CURLPX_RESOLVE_HOST; } @@ -839,7 +839,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, struct Curl_dns_entry *dns = NULL; struct Curl_addrinfo *hp = NULL; char dest[MAX_IPADR_LEN]; /* printable address */ - unsigned char *destination = NULL; + const unsigned char *destination = NULL; unsigned char desttype = 1, destlen = 4; unsigned char req[2]; CURLcode result; @@ -898,7 +898,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, desttype = 1; /* ATYP: IPv4 = 1 */ destlen = 4; saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; - destination = (unsigned char *)&saddr_in->sin_addr.s_addr; + destination = (const unsigned char *)&saddr_in->sin_addr.s_addr; CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (locally resolved)", dest, sx->remote_port); } @@ -908,7 +908,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, desttype = 4; /* ATYP: IPv6 = 4 */ destlen = 16; saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; - destination = (unsigned char *)&saddr_in6->sin6_addr.s6_addr; + destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr; CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%d (locally resolved)", dest, sx->remote_port); } @@ -932,8 +932,8 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, goto out; } /* PORT MSB+LSB */ - req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffu); - req[1] = (unsigned char)(sx->remote_port & 0xffu); + req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffU); + req[1] = (unsigned char)(sx->remote_port & 0xffU); result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) { presult = CURLPX_SEND_REQUEST; @@ -1287,7 +1287,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, else if(sx->state != SOCKS_ST_SUCCESS) goto out; -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { struct ip_quadruple ipquad; bool is_ipv6; @@ -1307,7 +1307,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, cf->connected = TRUE; out: - *done = cf->connected; + *done = (bool)cf->connected; return result; } @@ -1342,7 +1342,6 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, static void socks_proxy_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGASSERT(cf->next); cf->connected = FALSE; socks_proxy_cf_free(cf); diff --git a/vendor/curl/lib/socks_gssapi.c b/vendor/curl/lib/socks_gssapi.c index 2da1171..962fcd0 100644 --- a/vendor/curl/lib/socks_gssapi.c +++ b/vendor/curl/lib/socks_gssapi.c @@ -33,7 +33,7 @@ #include "connect.h" #include "curlx/nonblock.h" #include "socks.h" -#include "strdup.h" +#include "curlx/strdup.h" #if defined(__GNUC__) && defined(__APPLE__) #pragma GCC diagnostic push @@ -135,7 +135,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, /* prepare service name */ if(strchr(serviceptr, '/')) { service.length = serviceptr_length; - service.value = Curl_memdup(serviceptr, service.length); + service.value = curlx_memdup(serviceptr, service.length); if(!service.value) return CURLE_OUT_OF_MEMORY; @@ -168,8 +168,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, (void)curlx_nonblock(sock, FALSE); - /* As long as we need to keep sending some context info, and there is no */ - /* errors, keep sending it... */ + /* As long as we need to keep sending some context info, and there is no + * errors, keep sending it... */ for(;;) { gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, @@ -188,7 +188,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_init_sec_context") || - /* the size needs to fit in a 16 bit field */ + /* the size needs to fit in a 16-bit field */ (gss_send_token.length > 0xffff)) { gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_send_token); @@ -317,7 +317,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } infof(data, "SOCKS5 server authenticated user %.*s with GSS-API.", - (int)gss_send_token.length, (char *)gss_send_token.value); + (int)gss_send_token.length, (const char *)gss_send_token.value); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); @@ -374,7 +374,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } else { gss_send_token.length = 1; - gss_send_token.value = Curl_memdup(&gss_enc, gss_send_token.length); + gss_send_token.value = curlx_memdup(&gss_enc, gss_send_token.length); if(!gss_send_token.value) { Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; @@ -515,7 +515,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, (void)curlx_nonblock(sock, TRUE); infof(data, "SOCKS5 access with%s protection granted.", - (socksreq[0] == 0) ? "out GSS-API data": + (socksreq[0] == 0) ? "out GSS-API data" : ((socksreq[0] == 1) ? " GSS-API integrity" : " GSS-API confidentiality")); diff --git a/vendor/curl/lib/socks_sspi.c b/vendor/curl/lib/socks_sspi.c index 0f3cfce..cc0e043 100644 --- a/vendor/curl/lib/socks_sspi.c +++ b/vendor/curl/lib/socks_sspi.c @@ -56,52 +56,70 @@ static int check_sspi_err(struct Curl_easy *data, } /* This is the SSPI-using version of this function */ -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + CredHandle *cred_handle, + char **service_namep) { struct connectdata *conn = cf->conn; - curl_socket_t sock = conn->sock[cf->sockindex]; - CURLcode code; - size_t actualread; - size_t written; - CURLcode result; - /* Needs GSS-API authentication */ - SECURITY_STATUS status; - unsigned long sspi_ret_flags = 0; - unsigned char gss_enc; - SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3]; - SecBufferDesc input_desc, output_desc, wrap_desc; - SecPkgContext_Sizes sspi_sizes; - CredHandle cred_handle; - CtxtHandle sspi_context; - PCtxtHandle context_handle = NULL; - SecPkgCredentials_Names names; - char *service_name = NULL; - unsigned short us_length; - unsigned long qop; - unsigned char socksreq[4]; /* room for GSS-API exchange header only */ const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; - uint8_t *etbuf; - size_t etbuf_size; - - /* GSS-API request looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ + data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + SECURITY_STATUS status; /* prepare service name */ if(strchr(service, '/')) - service_name = curlx_strdup(service); + *service_namep = curlx_strdup(service); else - service_name = curl_maprintf("%s/%s", - service, conn->socks_proxy.host.name); - if(!service_name) + *service_namep = curl_maprintf("%s/%s", + service, conn->socks_proxy.host.name); + if(!*service_namep) return CURLE_OUT_OF_MEMORY; + status = + Curl_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *)CURL_UNCONST(TEXT("Kerberos")), + SECPKG_CRED_OUTBOUND, + NULL, NULL, NULL, NULL, + cred_handle, NULL); + + if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { + failf(data, "Failed to acquire credentials."); + return CURLE_COULDNT_CONNECT; + } + + return CURLE_OK; +} + +static CURLcode socks5_free_token(SecBuffer *send_token, + CURLcode result) +{ + if(send_token->pvBuffer) { + Curl_pSecFn->FreeContextBuffer(send_token->pvBuffer); + send_token->pvBuffer = NULL; + } + return result; +} + +static CURLcode socks5_sspi_loop(struct Curl_cfilter *cf, + struct Curl_easy *data, + CredHandle *cred_handle, + CtxtHandle *sspi_context, + char *service_name, + unsigned long *sspi_ret_flagsp) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result = CURLE_OK; + CURLcode code; + SECURITY_STATUS status; + SecBuffer sspi_send_token, sspi_recv_token; + SecBufferDesc input_desc, output_desc; + PCtxtHandle context_handle = NULL; + unsigned short us_length; + size_t actualread; + size_t written; + unsigned char socksreq[4]; + input_desc.cBuffers = 1; input_desc.pBuffers = &sspi_recv_token; input_desc.ulVersion = SECBUFFER_VERSION; @@ -118,50 +136,17 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_send_token.cbBuffer = 0; sspi_send_token.pvBuffer = NULL; - sspi_w_token[0].pvBuffer = - sspi_w_token[1].pvBuffer = - sspi_w_token[2].pvBuffer = NULL; - - wrap_desc.cBuffers = 3; - wrap_desc.pBuffers = sspi_w_token; - wrap_desc.ulVersion = SECBUFFER_VERSION; - - memset(&cred_handle, 0, sizeof(cred_handle)); - memset(&sspi_context, 0, sizeof(sspi_context)); - - names.sUserName = NULL; - - etbuf = NULL; - etbuf_size = 0; - - status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT("Kerberos")), - SECPKG_CRED_OUTBOUND, - NULL, NULL, NULL, NULL, - &cred_handle, NULL); - - if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { - failf(data, "Failed to acquire credentials."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - (void)curlx_nonblock(sock, FALSE); - /* As long as we need to keep sending some context info, and there is no */ - /* errors, keep sending it... */ for(;;) { - TCHAR *sname; - - sname = curlx_convert_UTF8_to_tchar(service_name); + TCHAR *sname = curlx_convert_UTF8_to_tchar(service_name); if(!sname) { - result = CURLE_OUT_OF_MEMORY; - goto error; + curlx_free(sspi_recv_token.pvBuffer); + return socks5_free_token(&sspi_send_token, CURLE_OUT_OF_MEMORY); } status = - Curl_pSecFn->InitializeSecurityContext(&cred_handle, context_handle, + Curl_pSecFn->InitializeSecurityContext(cred_handle, context_handle, sname, ISC_REQ_MUTUAL_AUTH | ISC_REQ_ALLOCATE_MEMORY | @@ -169,28 +154,25 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, ISC_REQ_REPLAY_DETECT, 0, SECURITY_NATIVE_DREP, &input_desc, 0, - &sspi_context, + sspi_context, &output_desc, - &sspi_ret_flags, NULL); + sspi_ret_flagsp, NULL); curlx_free(sname); - Curl_safefree(sspi_recv_token.pvBuffer); sspi_recv_token.cbBuffer = 0; if(check_sspi_err(data, status, "InitializeSecurityContext")) { failf(data, "Failed to initialise security context."); - result = CURLE_COULDNT_CONNECT; - goto error; + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } if(sspi_send_token.cbBuffer) { socksreq[0] = 1; /* GSS-API subnegotiation version */ socksreq[1] = 1; /* authentication message type */ if(sspi_send_token.cbBuffer > 0xffff) { - /* needs to fit in an unsigned 16 bit field */ - result = CURLE_COULDNT_CONNECT; - goto error; + /* needs to fit in an unsigned 16-bit field */ + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } us_length = htons((unsigned short)sspi_send_token.cbBuffer); memcpy(socksreq + 2, &us_length, sizeof(short)); @@ -198,8 +180,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); if(code || (written != 4)) { failf(data, "Failed to send SSPI authentication request."); - result = CURLE_COULDNT_CONNECT; - goto error; + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } code = Curl_conn_cf_send(cf->next, data, @@ -207,15 +188,12 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_send_token.cbBuffer, FALSE, &written); if(code || (sspi_send_token.cbBuffer != written)) { failf(data, "Failed to send SSPI authentication token."); - result = CURLE_COULDNT_CONNECT; - goto error; + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } } - if(sspi_send_token.pvBuffer) { - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - sspi_send_token.pvBuffer = NULL; - } + if(sspi_send_token.pvBuffer) + socks5_free_token(&sspi_send_token, CURLE_OK); sspi_send_token.cbBuffer = 0; Curl_safefree(sspi_recv_token.pvBuffer); @@ -224,37 +202,22 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(status != SEC_I_CONTINUE_NEEDED) break; - /* analyse response */ - - /* GSS-API response looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ - result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI authentication response."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; + return result ? result : CURLE_COULDNT_CONNECT; } - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ + if(socksreq[1] == 255) { failf(data, "User was rejected by the SOCKS5 server (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } - if(socksreq[1] != 1) { /* status / message type */ + if(socksreq[1] != 1) { failf(data, "Invalid SSPI authentication response type (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq + 2, sizeof(short)); @@ -263,151 +226,121 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_recv_token.cbBuffer = us_length; sspi_recv_token.pvBuffer = curlx_malloc(us_length); - if(!sspi_recv_token.pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!sspi_recv_token.pvBuffer) + return CURLE_OUT_OF_MEMORY; + result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, sspi_recv_token.cbBuffer, &actualread); if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI authentication token."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_recv_token.pvBuffer); + return result ? result : CURLE_COULDNT_CONNECT; } - context_handle = &sspi_context; + context_handle = sspi_context; } - Curl_safefree(service_name); + return CURLE_OK; +} - /* Everything is good so far, user was authenticated! */ - status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle, - SECPKG_CRED_ATTR_NAMES, - &names); - if(check_sspi_err(data, status, "QueryCredentialAttributes")) { - failf(data, "Failed to determine username."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - else { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char *user_utf8 = curlx_convert_tchar_to_UTF8(names.sUserName); - infof(data, "SOCKS5 server authenticated user %s with GSS-API.", - (user_utf8 ? user_utf8 : "(unknown)")); - curlx_free(user_utf8); -#endif - Curl_pSecFn->FreeContextBuffer(names.sUserName); - names.sUserName = NULL; - } +static CURLcode socks5_free(SecBuffer *sspi_w_token, + CURLcode result) +{ + Curl_safefree(sspi_w_token[0].pvBuffer); + Curl_safefree(sspi_w_token[1].pvBuffer); + Curl_safefree(sspi_w_token[2].pvBuffer); + return result; +} - /* Do encryption */ - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 2; /* encryption message type */ +static CURLcode socks5_sspi_encrypt(struct Curl_cfilter *cf, + struct Curl_easy *data, + CtxtHandle *sspi_context, + unsigned long sspi_ret_flags) +{ + CURLcode result = CURLE_OK; + CURLcode code; + SECURITY_STATUS status; + unsigned char gss_enc; + SecBuffer sspi_w_token[3]; + SecBufferDesc wrap_desc; + SecPkgContext_Sizes sspi_sizes; + unsigned short us_length; + unsigned long qop; + unsigned char socksreq[4]; + uint8_t *etbuf = NULL; + size_t etbuf_size = 0; + size_t actualread; + size_t written; - gss_enc = 0; /* no data protection */ - /* do confidentiality protection if supported */ + gss_enc = 0; if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY) gss_enc = 2; - /* else do integrity protection */ else if(sspi_ret_flags & ISC_REQ_INTEGRITY) gss_enc = 1; infof(data, "SOCKS5 server supports GSS-API %s data protection.", (gss_enc == 0) ? "no" : - ((gss_enc == 1) ? "integrity" : "confidentiality") ); - - /* - * Sending the encryption type in clear seems wrong. It should be - * protected with gss_seal()/gss_wrap(). See RFC1961 extract below - * The NEC reference implementations on which this is based is - * therefore at fault - * - * +------+------+------+.......................+ - * + ver | mtyp | len | token | - * +------+------+------+.......................+ - * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | - * +------+------+------+.......................+ - * - * Where: - * - * - "ver" is the protocol version number, here 1 to represent the - * first version of the SOCKS/GSS-API protocol - * - * - "mtyp" is the message type, here 2 to represent a protection - * -level negotiation message - * - * - "len" is the length of the "token" field in octets - * - * - "token" is the GSS-API encapsulated protection level - * - * The token is produced by encapsulating an octet containing the - * required protection level using gss_seal()/gss_wrap() with conf_req - * set to FALSE. The token is verified using gss_unseal()/ - * gss_unwrap(). - * - */ + ((gss_enc == 1) ? "integrity" : "confidentiality")); + + sspi_w_token[0].pvBuffer = + sspi_w_token[1].pvBuffer = + sspi_w_token[2].pvBuffer = NULL; + + wrap_desc.cBuffers = 3; + wrap_desc.pBuffers = sspi_w_token; + wrap_desc.ulVersion = SECBUFFER_VERSION; + + socksreq[0] = 1; + socksreq[1] = 2; if(data->set.socks5_gssapi_nec) { us_length = htons((unsigned short)1); memcpy(socksreq + 2, &us_length, sizeof(short)); } else { - status = Curl_pSecFn->QueryContextAttributes(&sspi_context, + status = Curl_pSecFn->QueryContextAttributes(sspi_context, SECPKG_ATTR_SIZES, &sspi_sizes); if(check_sspi_err(data, status, "QueryContextAttributes")) { failf(data, "Failed to query security context attributes."); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer; sspi_w_token[0].BufferType = SECBUFFER_TOKEN; sspi_w_token[0].pvBuffer = curlx_malloc(sspi_sizes.cbSecurityTrailer); - if(!sspi_w_token[0].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!sspi_w_token[0].pvBuffer) + return CURLE_OUT_OF_MEMORY; sspi_w_token[1].cbBuffer = 1; + sspi_w_token[1].BufferType = SECBUFFER_DATA; sspi_w_token[1].pvBuffer = curlx_malloc(1); - if(!sspi_w_token[1].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!sspi_w_token[1].pvBuffer) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1); sspi_w_token[2].BufferType = SECBUFFER_PADDING; sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize; sspi_w_token[2].pvBuffer = curlx_malloc(sspi_sizes.cbBlockSize); - if(!sspi_w_token[2].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - status = Curl_pSecFn->EncryptMessage(&sspi_context, + if(!sspi_w_token[2].pvBuffer) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); + + status = Curl_pSecFn->EncryptMessage(sspi_context, KERB_WRAP_NO_ENCRYPT, &wrap_desc, 0); - if(check_sspi_err(data, status, "EncryptMessage")) { - failf(data, "Failed to query security context attributes."); - result = CURLE_COULDNT_CONNECT; - goto error; - } + if(check_sspi_err(data, status, "EncryptMessage")) + return socks5_free(sspi_w_token, CURLE_COULDNT_CONNECT); etbuf_size = sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer + - sspi_w_token[2].cbBuffer; - if(etbuf_size > 0xffff) { - /* needs to fit in an unsigned 16 bit field */ - result = CURLE_COULDNT_CONNECT; - goto error; - } + sspi_w_token[2].cbBuffer; + if(etbuf_size > 0xffff) + return socks5_free(sspi_w_token, CURLE_COULDNT_CONNECT); + etbuf = curlx_malloc(etbuf_size); - if(!etbuf) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!etbuf) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); memcpy(etbuf, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); memcpy(etbuf + sspi_w_token[0].cbBuffer, @@ -415,12 +348,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(etbuf + sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer, sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer); - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; - Curl_safefree(sspi_w_token[1].pvBuffer); - sspi_w_token[1].cbBuffer = 0; - Curl_safefree(sspi_w_token[2].pvBuffer); - sspi_w_token[2].cbBuffer = 0; + (void)socks5_free(sspi_w_token, CURLE_OK); us_length = htons((unsigned short)etbuf_size); memcpy(socksreq + 2, &us_length, sizeof(short)); @@ -429,8 +357,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); if(code || (written != 4)) { failf(data, "Failed to send SSPI encryption request."); - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(etbuf); + return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { @@ -438,42 +366,35 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, code = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &written); if(code || (written != 1)) { failf(data, "Failed to send SSPI encryption type."); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } } else { - code = Curl_conn_cf_send(cf->next, data, etbuf, etbuf_size, - FALSE, &written); + code = Curl_conn_cf_send(cf->next, data, etbuf, etbuf_size, FALSE, + &written); + curlx_free(etbuf); if(code || (etbuf_size != written)) { failf(data, "Failed to send SSPI encryption type."); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } - Curl_safefree(etbuf); } result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI encryption response."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; + return result ? result : CURLE_COULDNT_CONNECT; } - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ + if(socksreq[1] == 255) { failf(data, "User was rejected by the SOCKS5 server (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } - if(socksreq[1] != 2) { /* status / message type */ + if(socksreq[1] != 2) { failf(data, "Invalid SSPI encryption response type (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq + 2, sizeof(short)); @@ -481,19 +402,16 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_w_token[0].cbBuffer = us_length; sspi_w_token[0].pvBuffer = curlx_malloc(us_length); - if(!sspi_w_token[0].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!sspi_w_token[0].pvBuffer) + return CURLE_OUT_OF_MEMORY; result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer, &actualread); if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI encryption type."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_w_token[0].pvBuffer); + return result ? result : CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { @@ -503,86 +421,108 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_w_token[1].cbBuffer = 0; sspi_w_token[1].pvBuffer = NULL; - status = Curl_pSecFn->DecryptMessage(&sspi_context, &wrap_desc, + status = Curl_pSecFn->DecryptMessage(sspi_context, &wrap_desc, 0, &qop); - /* since sspi_w_token[1].pvBuffer is allocated by the SSPI in this case, it - must be freed in this block using FreeContextBuffer() instead of - potentially in error cleanup using curlx_free(). */ - if(check_sspi_err(data, status, "DecryptMessage")) { - failf(data, "Failed to query security context attributes."); - if(sspi_w_token[1].pvBuffer) { + if(sspi_w_token[1].pvBuffer) Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - } - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } if(sspi_w_token[1].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[1].cbBuffer); - if(sspi_w_token[1].pvBuffer) { + if(sspi_w_token[1].pvBuffer) Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - } - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - sspi_w_token[1].cbBuffer = 0; } else { if(sspi_w_token[0].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[0].cbBuffer); - result = CURLE_COULDNT_CONNECT; - goto error; + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; } - (void)curlx_nonblock(sock, TRUE); + curlx_free(sspi_w_token[0].pvBuffer); infof(data, "SOCKS5 access with%s protection granted BUT NOT USED.", - (socksreq[0] == 0) ? "out GSS-API data": + (socksreq[0] == 0) ? "out GSS-API data" : ((socksreq[0] == 1) ? " GSS-API integrity" : " GSS-API confidentiality")); - /* For later use if encryption is required - conn->socks5_gssapi_enctype = socksreq[0]; - if(socksreq[0] != 0) - conn->socks5_sspi_context = sspi_context; - else { - Curl_pSecFn->DeleteSecurityContext(&sspi_context); - conn->socks5_sspi_context = sspi_context; - } - */ + return CURLE_OK; +} + +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result; + SECURITY_STATUS status; + CredHandle cred_handle; + CtxtHandle sspi_context; + SecPkgCredentials_Names names; + char *service_name = NULL; + unsigned long sspi_ret_flags = 0; + + memset(&cred_handle, 0, sizeof(cred_handle)); + memset(&sspi_context, 0, sizeof(sspi_context)); + names.sUserName = NULL; + + result = socks5_sspi_setup(cf, data, &cred_handle, &service_name); + if(result) + goto error; + + result = socks5_sspi_loop(cf, data, &cred_handle, &sspi_context, + service_name, &sspi_ret_flags); + if(result) + goto error; + + Curl_safefree(service_name); + + status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle, + SECPKG_CRED_ATTR_NAMES, + &names); + if(check_sspi_err(data, status, "QueryCredentialAttributes")) { + failf(data, "Failed to determine username."); + result = CURLE_COULDNT_CONNECT; + goto error; + } + else { + VERBOSE(char *user_utf8 = curlx_convert_tchar_to_UTF8(names.sUserName)); + infof(data, "SOCKS5 server authenticated user %s with GSS-API.", + (user_utf8 ? user_utf8 : "(unknown)")); + VERBOSE(curlx_free(user_utf8)); + Curl_pSecFn->FreeContextBuffer(names.sUserName); + names.sUserName = NULL; + } + + result = socks5_sspi_encrypt(cf, data, &sspi_context, sspi_ret_flags); + if(result) + goto error; + (void)curlx_nonblock(sock, TRUE); Curl_pSecFn->DeleteSecurityContext(&sspi_context); Curl_pSecFn->FreeCredentialsHandle(&cred_handle); return CURLE_OK; + error: (void)curlx_nonblock(sock, TRUE); curlx_free(service_name); Curl_pSecFn->DeleteSecurityContext(&sspi_context); Curl_pSecFn->FreeCredentialsHandle(&cred_handle); - curlx_free(sspi_recv_token.pvBuffer); - if(sspi_send_token.pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); if(names.sUserName) Curl_pSecFn->FreeContextBuffer(names.sUserName); - curlx_free(sspi_w_token[0].pvBuffer); - curlx_free(sspi_w_token[1].pvBuffer); - curlx_free(sspi_w_token[2].pvBuffer); - curlx_free(etbuf); return result; } #endif diff --git a/vendor/curl/lib/splay.c b/vendor/curl/lib/splay.c index 5e4cd54..ddab7a4 100644 --- a/vendor/curl/lib/splay.c +++ b/vendor/curl/lib/splay.c @@ -241,7 +241,7 @@ int Curl_splayremove(struct Curl_tree *t, to remove, as otherwise we might be trying to remove a node that is not actually in the tree. - We cannot just compare the keys here as a double remove in quick + We cannot compare the keys here as a double remove in quick succession of a node with key != SPLAY_SUBNODE && same != NULL could return the same key but a different node. */ DEBUGASSERT(t == removenode); @@ -252,7 +252,7 @@ int Curl_splayremove(struct Curl_tree *t, remove the root node of a list of nodes with identical keys. */ x = t->samen; if(x != t) { - /* 'x' is the new root node, we just make it use the root node's + /* 'x' is the new root node, we make it use the root node's smaller/larger links */ x->key = t->key; diff --git a/vendor/curl/lib/splay.h b/vendor/curl/lib/splay.h index 9ed96f6..c6623f2 100644 --- a/vendor/curl/lib/splay.h +++ b/vendor/curl/lib/splay.h @@ -42,7 +42,7 @@ struct Curl_tree *Curl_splay(const struct curltime *pkey, struct Curl_tree *Curl_splayinsert(const struct curltime *pkey, struct Curl_tree *t, - struct Curl_tree *newnode); + struct Curl_tree *node); struct Curl_tree *Curl_splaygetbest(const struct curltime *pkey, struct Curl_tree *t, diff --git a/vendor/curl/lib/strcase.c b/vendor/curl/lib/strcase.c index 34ccae3..9f70f41 100644 --- a/vendor/curl/lib/strcase.c +++ b/vendor/curl/lib/strcase.c @@ -116,7 +116,7 @@ void Curl_strntolower(char *dest, const char *src, size_t n) /* Compare case-sensitive null-terminated strings, taking care of possible * null pointers. Return true if arguments match. */ -bool Curl_safecmp(char *a, char *b) +bool Curl_safecmp(const char *a, const char *b) { if(a && b) return !strcmp(a, b); diff --git a/vendor/curl/lib/strcase.h b/vendor/curl/lib/strcase.h index 3a7f126..5429981 100644 --- a/vendor/curl/lib/strcase.h +++ b/vendor/curl/lib/strcase.h @@ -35,7 +35,7 @@ char Curl_raw_tolower(char in); void Curl_strntoupper(char *dest, const char *src, size_t n); void Curl_strntolower(char *dest, const char *src, size_t n); -bool Curl_safecmp(char *a, char *b); -int Curl_timestrcmp(const char *first, const char *second); +bool Curl_safecmp(const char *a, const char *b); +int Curl_timestrcmp(const char *a, const char *b); #endif /* HEADER_CURL_STRCASE_H */ diff --git a/vendor/curl/lib/strequal.c b/vendor/curl/lib/strequal.c index 98cecdc..a352fda 100644 --- a/vendor/curl/lib/strequal.c +++ b/vendor/curl/lib/strequal.c @@ -42,7 +42,7 @@ static int casecompare(const char *first, const char *second) second++; } /* If we are here either the strings are the same or the length is different. - We can just test if the "current" character is non-zero for one and zero + We can test if the "current" character is non-zero for one and zero for the other. Note that the characters may not be exactly the same even if they match, we only want to compare zero-ness. */ return !*first == !*second; @@ -73,23 +73,23 @@ static int ncasecompare(const char *first, const char *second, size_t max) */ /* --- public function --- */ -int curl_strequal(const char *first, const char *second) +int curl_strequal(const char *s1, const char *s2) { - if(first && second) + if(s1 && s2) /* both pointers point to something then compare them */ - return casecompare(first, second); + return casecompare(s1, s2); /* if both pointers are NULL then treat them as equal */ - return NULL == first && NULL == second; + return NULL == s1 && NULL == s2; } /* --- public function --- */ -int curl_strnequal(const char *first, const char *second, size_t max) +int curl_strnequal(const char *s1, const char *s2, size_t n) { - if(first && second) + if(s1 && s2) /* both pointers point to something then compare them */ - return ncasecompare(first, second, max); + return ncasecompare(s1, s2, n); /* if both pointers are NULL then treat them as equal if max is non-zero */ - return NULL == first && NULL == second && max; + return NULL == s1 && NULL == s2 && n; } diff --git a/vendor/curl/lib/strerror.c b/vendor/curl/lib/strerror.c index b281b18..1e97c28 100644 --- a/vendor/curl/lib/strerror.c +++ b/vendor/curl/lib/strerror.c @@ -33,7 +33,7 @@ const char *curl_easy_strerror(CURLcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLE_OK: return "No error"; @@ -325,7 +325,7 @@ const char *curl_easy_strerror(CURLcode error) const char *curl_multi_strerror(CURLMcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLM_CALL_MULTI_PERFORM: return "Please call curl_multi_perform() soon"; @@ -384,7 +384,7 @@ const char *curl_multi_strerror(CURLMcode error) const char *curl_share_strerror(CURLSHcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLSHE_OK: return "No error"; @@ -419,7 +419,7 @@ const char *curl_share_strerror(CURLSHcode error) const char *curl_url_strerror(CURLUcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLUE_OK: return "No error"; @@ -541,15 +541,14 @@ const char *Curl_sspi_strerror(SECURITY_STATUS err, char *buf, size_t buflen) DWORD old_win_err = GetLastError(); #endif int old_errno = errno; - const char *txt; + VERBOSE(const char *txt); if(!buflen) return NULL; *buf = '\0'; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - +#ifdef CURLVERBOSE switch(err) { case SEC_E_OK: txt = "No error"; @@ -657,13 +656,11 @@ const char *Curl_sspi_strerror(SECURITY_STATUS err, char *buf, size_t buflen) else curl_msnprintf(buf, buflen, "%s (0x%08lx)", txt, err); } - -#else +#else /* CURLVERBOSE */ if(err == SEC_E_OK) - txt = "No error"; + curlx_strcopy(buf, buflen, STRCONST("No error")); else - txt = "Error"; - curlx_strcopy(buf, buflen, txt, strlen(txt)); + curlx_strcopy(buf, buflen, STRCONST("Error")); #endif if(errno != old_errno) diff --git a/vendor/curl/lib/system_win32.c b/vendor/curl/lib/system_win32.c index 0e03e7c..1b05235 100644 --- a/vendor/curl/lib/system_win32.c +++ b/vendor/curl/lib/system_win32.c @@ -26,25 +26,15 @@ #ifdef _WIN32 #include "system_win32.h" -#include "curlx/version_win32.h" #include "curl_sspi.h" - -#ifndef HAVE_IF_NAMETOINDEX -/* Handle of iphlpapp.dll */ -static HMODULE s_hIpHlpApiDll = NULL; - -/* Pointer to the if_nametoindex function */ -IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL; - -/* This is used to dynamically load DLLs */ -static HMODULE curl_load_library(LPCTSTR filename); -#endif +#include "curlx/timeval.h" +#include "curlx/version_win32.h" /* for curlx_verify_windows_init() */ /* Curl_win32_init() performs Win32 global initialization */ CURLcode Curl_win32_init(long flags) { /* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which - is just for Winsock at the moment. Any required Win32 initialization + is for Winsock at the moment. Any required Win32 initialization should take place after this block. */ if(flags & CURL_GLOBAL_WIN32) { #ifdef USE_WINSOCK @@ -57,7 +47,7 @@ CURLcode Curl_win32_init(long flags) if(res) /* Tell the user that we could not find a usable */ - /* winsock.dll. */ + /* winsock.dll. */ return CURLE_FAILED_INIT; /* Confirm that the Windows Sockets DLL supports what we need.*/ @@ -88,43 +78,14 @@ CURLcode Curl_win32_init(long flags) } #endif -#ifndef HAVE_IF_NAMETOINDEX - s_hIpHlpApiDll = curl_load_library(TEXT("iphlpapi.dll")); - if(s_hIpHlpApiDll) { - /* Get the address of the if_nametoindex function */ - IF_NAMETOINDEX_FN pIfNameToIndex = - CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN, - GetProcAddress(s_hIpHlpApiDll, "if_nametoindex")); - - if(pIfNameToIndex) - Curl_if_nametoindex = pIfNameToIndex; - } -#endif - - /* curlx_verify_windows_version must be called during init at least once - because it has its own initialization routine. */ - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - Curl_isVistaOrGreater = TRUE; - } - else - Curl_isVistaOrGreater = FALSE; - - QueryPerformanceFrequency(&Curl_freq); + curlx_verify_windows_init(); + curlx_now_init(); return CURLE_OK; } /* Curl_win32_cleanup() is the opposite of Curl_win32_init() */ void Curl_win32_cleanup(long init_flags) { -#ifndef HAVE_IF_NAMETOINDEX - if(s_hIpHlpApiDll) { - FreeLibrary(s_hIpHlpApiDll); - s_hIpHlpApiDll = NULL; - Curl_if_nametoindex = NULL; - } -#endif - #ifdef USE_WINDOWS_SSPI Curl_sspi_global_cleanup(); #endif @@ -136,104 +97,4 @@ void Curl_win32_cleanup(long init_flags) } } -#ifndef HAVE_IF_NAMETOINDEX - -#ifndef LOAD_WITH_ALTERED_SEARCH_PATH -#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 -#endif - -#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 -#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 -#endif - -/* We use our own typedef here since some headers might lack these */ -typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD); - -/* See function definitions in winbase.h */ -#ifdef UNICODE -# define LOADLIBARYEX "LoadLibraryExW" -#else -# define LOADLIBARYEX "LoadLibraryExA" -#endif - -/* - * curl_load_library() - * - * This is used to dynamically load DLLs using the most secure method available - * for the version of Windows that we are running on. - * - * Parameters: - * - * filename [in] - The filename or full path of the DLL to load. If only the - * filename is passed then the DLL will be loaded from the - * Windows system directory. - * - * Returns the handle of the module on success; otherwise NULL. - */ -static HMODULE curl_load_library(LPCTSTR filename) -{ -#ifndef CURL_WINDOWS_UWP - HMODULE hModule = NULL; - LOADLIBRARYEX_FN pLoadLibraryEx = NULL; - - /* Get a handle to kernel32 so we can access its functions at runtime */ - HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32")); - if(!hKernel32) - return NULL; - - /* Attempt to find LoadLibraryEx() which is only available on Windows 2000 - and above */ - pLoadLibraryEx = - CURLX_FUNCTION_CAST(LOADLIBRARYEX_FN, - GetProcAddress(hKernel32, LOADLIBARYEX)); - - /* Detect if there is already a path in the filename and load the library if - there is. Note: Both back slashes and forward slashes have been supported - since the earlier days of DOS at an API level although they are not - supported by command prompt */ - if(_tcspbrk(filename, TEXT("\\/"))) { - /** !checksrc! disable BANNEDFUNC 1 **/ - hModule = pLoadLibraryEx ? - pLoadLibraryEx(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : - LoadLibrary(filename); - } - /* Detect if KB2533623 is installed, as LOAD_LIBRARY_SEARCH_SYSTEM32 is only - supported on Windows Vista, Windows Server 2008, Windows 7 and Windows - Server 2008 R2 with this patch or natively on Windows 8 and above */ - else if(pLoadLibraryEx && GetProcAddress(hKernel32, "AddDllDirectory")) { - /* Load the DLL from the Windows system directory */ - hModule = pLoadLibraryEx(filename, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); - } - else { - /* Attempt to get the Windows system path */ - UINT systemdirlen = GetSystemDirectory(NULL, 0); - if(systemdirlen) { - /* Allocate space for the full DLL path (Room for the null-terminator - is included in systemdirlen) */ - size_t filenamelen = _tcslen(filename); - TCHAR *path = curlx_malloc(sizeof(TCHAR) * - (systemdirlen + 1 + filenamelen)); - if(path && GetSystemDirectory(path, systemdirlen)) { - /* Calculate the full DLL path */ - _tcscpy(path + _tcslen(path), TEXT("\\")); - _tcscpy(path + _tcslen(path), filename); - - /* Load the DLL from the Windows system directory */ - /** !checksrc! disable BANNEDFUNC 1 **/ - hModule = pLoadLibraryEx ? - pLoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : - LoadLibrary(path); - } - curlx_free(path); - } - } - return hModule; -#else - /* the Universal Windows Platform (UWP) cannot do this */ - (void)filename; - return NULL; -#endif -} -#endif /* !HAVE_IF_NAMETOINDEX */ - #endif /* _WIN32 */ diff --git a/vendor/curl/lib/system_win32.h b/vendor/curl/lib/system_win32.h index c7df2a5..8a51f09 100644 --- a/vendor/curl/lib/system_win32.h +++ b/vendor/curl/lib/system_win32.h @@ -26,21 +26,11 @@ #include "curl_setup.h" #ifdef _WIN32 - extern LARGE_INTEGER Curl_freq; -extern bool Curl_isVistaOrGreater; CURLcode Curl_win32_init(long flags); void Curl_win32_cleanup(long init_flags); - -#ifndef HAVE_IF_NAMETOINDEX -/* We use our own typedef here since some headers might lack this */ -typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *); - -/* This is used instead of if_nametoindex if available on Windows */ -extern IF_NAMETOINDEX_FN Curl_if_nametoindex; -#endif -#else /* !_WIN32 */ +#else #define Curl_win32_init(x) CURLE_OK #endif /* _WIN32 */ diff --git a/vendor/curl/lib/telnet.c b/vendor/curl/lib/telnet.c index cca97cf..5bae99e 100644 --- a/vendor/curl/lib/telnet.c +++ b/vendor/curl/lib/telnet.c @@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "telnet.h" #ifndef CURL_DISABLE_TELNET @@ -45,13 +47,10 @@ #include #endif -#include "urldata.h" #include "url.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" -#include "telnet.h" -#include "connect.h" #include "progress.h" #include "arpa_telnet.h" #include "select.h" @@ -59,29 +58,25 @@ #define SUBBUFSIZE 512 -#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer -#define CURL_SB_TERM(x) \ - do { \ - x->subend = x->subpointer; \ - CURL_SB_CLEAR(x); \ +#define CURL_SB_CLEAR(x) x->subpointer = (x)->subbuffer +#define CURL_SB_TERM(x) \ + do { \ + (x)->subend = (x)->subpointer; \ + CURL_SB_CLEAR(x); \ } while(0) -#define CURL_SB_ACCUM(x, c) \ - do { \ - if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \ - *x->subpointer++ = (c); \ +#define CURL_SB_ACCUM(x, c) \ + do { \ + if((x)->subpointer < ((x)->subbuffer + sizeof((x)->subbuffer))) \ + *(x)->subpointer++ = (c); \ } while(0) -#define CURL_SB_GET(x) ((*x->subpointer++) & 0xff) -#define CURL_SB_LEN(x) (x->subend - x->subpointer) +#define CURL_SB_GET(x) ((*(x)->subpointer++) & 0xff) +#define CURL_SB_LEN(x) ((x)->subend - (x)->subpointer) /* For posterity: #define CURL_SB_PEEK(x) ((*x->subpointer)&0xff) #define CURL_SB_EOF(x) (x->subpointer >= x->subend) */ -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define printoption(a, b, c, d) Curl_nop_stmt -#endif - /* For negotiation compliant to RFC 1143 */ #define CURL_NO 0 #define CURL_YES 1 @@ -119,8 +114,8 @@ struct TELNET { int himq[256]; int him_preferred[256]; int subnegotiation[256]; - char *subopt_ttype; /* Set with suboption TTYPE */ - char *subopt_xdisploc; /* Set with suboption XDISPLOC */ + const char *subopt_ttype; /* Set with suboption TTYPE */ + const char *subopt_xdisploc; /* Set with suboption XDISPLOC */ unsigned short subopt_wsx; /* Set with suboption NAWS */ unsigned short subopt_wsy; /* Set with suboption NAWS */ TelnetReceive telrcv_state; @@ -132,67 +127,44 @@ struct TELNET { unsigned char *subpointer, *subend; /* buffer for sub-options */ }; -static CURLcode telrcv(struct Curl_easy *data, - struct TELNET *tn, - const unsigned char *inbuf, /* Data received from - socket */ - ssize_t count); /* Number of bytes - received */ - -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifndef CURLVERBOSE +#define printoption(a, b, c, d) Curl_nop_stmt +#else static void printoption(struct Curl_easy *data, - const char *direction, - int cmd, int option); -#endif - -static void send_negotiation(struct Curl_easy *data, int cmd, int option); -static void set_local_option(struct Curl_easy *data, struct TELNET *tn, - int option, int newstate); -static void set_remote_option(struct Curl_easy *data, struct TELNET *tn, - int option, int newstate); - -static void printsub(struct Curl_easy *data, - int direction, unsigned char *pointer, - size_t length); -static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn); -static void sendsuboption(struct Curl_easy *data, - struct TELNET *tn, int option); - -static CURLcode telnet_do(struct Curl_easy *data, bool *done); -static CURLcode telnet_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode send_telnet_data(struct Curl_easy *data, - struct TELNET *tn, - char *buffer, ssize_t nread); - -/* - * TELNET protocol handler. - */ + const char *direction, int cmd, int option) +{ + if(data->set.verbose) { + if(cmd == CURL_IAC) { + if(CURL_TELCMD_OK(option)) + infof(data, "%s IAC %s", direction, CURL_TELCMD(option)); + else + infof(data, "%s IAC %d", direction, option); + } + else { + const char *fmt = (cmd == CURL_WILL) ? "WILL" : + (cmd == CURL_WONT) ? "WONT" : + (cmd == CURL_DO) ? "DO" : + (cmd == CURL_DONT) ? "DONT" : 0; + if(fmt) { + const char *opt; + if(CURL_TELOPT_OK(option)) + opt = CURL_TELOPT(option); + else if(option == CURL_TELOPT_EXOPL) + opt = "EXOPL"; + else + opt = NULL; -const struct Curl_handler Curl_handler_telnet = { - "telnet", /* scheme */ - ZERO_NULL, /* setup_connection */ - telnet_do, /* do_it */ - telnet_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_TELNET, /* defport */ - CURLPROTO_TELNET, /* protocol */ - CURLPROTO_TELNET, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ -}; + if(opt) + infof(data, "%s %s %s", direction, fmt, opt); + else + infof(data, "%s %s %d", direction, fmt, option); + } + else + infof(data, "%s %d %d", direction, cmd, option); + } + } +} +#endif /* !CURLVERBOSE */ static void telnet_easy_dtor(void *key, size_t klen, void *entry) { @@ -239,8 +211,8 @@ static CURLcode init_telnet(struct Curl_easy *data) */ tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES; - /* Set the subnegotiation fields to send information - just after negotiation passed (do/will) + /* Set the subnegotiation fields to send information after negotiation + passed (do/will) Default values are (0,0) initialized by calloc. According to the RFC1013 it is valid: @@ -255,59 +227,6 @@ static CURLcode init_telnet(struct Curl_easy *data) return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor); } -static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn) -{ - int i; - - for(i = 0; i < CURL_NTELOPTS; i++) { - if(i == CURL_TELOPT_ECHO) - continue; - - if(tn->us_preferred[i] == CURL_YES) - set_local_option(data, tn, i, CURL_YES); - - if(tn->him_preferred[i] == CURL_YES) - set_remote_option(data, tn, i, CURL_YES); - } -} - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void printoption(struct Curl_easy *data, - const char *direction, int cmd, int option) -{ - if(data->set.verbose) { - if(cmd == CURL_IAC) { - if(CURL_TELCMD_OK(option)) - infof(data, "%s IAC %s", direction, CURL_TELCMD(option)); - else - infof(data, "%s IAC %d", direction, option); - } - else { - const char *fmt = (cmd == CURL_WILL) ? "WILL" : - (cmd == CURL_WONT) ? "WONT" : - (cmd == CURL_DO) ? "DO" : - (cmd == CURL_DONT) ? "DONT" : 0; - if(fmt) { - const char *opt; - if(CURL_TELOPT_OK(option)) - opt = CURL_TELOPT(option); - else if(option == CURL_TELOPT_EXOPL) - opt = "EXOPL"; - else - opt = NULL; - - if(opt) - infof(data, "%s %s %s", direction, fmt, opt); - else - infof(data, "%s %s %d", direction, fmt, option); - } - else - infof(data, "%s %d %d", direction, cmd, option); - } - } -} -#endif - static void send_negotiation(struct Curl_easy *data, int cmd, int option) { unsigned char buf[3]; @@ -400,6 +319,95 @@ static void set_remote_option(struct Curl_easy *data, struct TELNET *tn, } } +static void set_local_option(struct Curl_easy *data, struct TELNET *tn, + int option, int newstate) +{ + if(newstate == CURL_YES) { + switch(tn->us[option]) { + case CURL_NO: + tn->us[option] = CURL_WANTYES; + send_negotiation(data, CURL_WILL, option); + break; + + case CURL_YES: + /* Already enabled */ + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Already negotiating for CURL_YES, queue the request */ + tn->usq[option] = CURL_OPPOSITE; + break; + case CURL_OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Error: already negotiating for enable */ + break; + case CURL_OPPOSITE: + tn->usq[option] = CURL_EMPTY; + break; + } + break; + } + } + else { /* NO */ + switch(tn->us[option]) { + case CURL_NO: + /* Already disabled */ + break; + + case CURL_YES: + tn->us[option] = CURL_WANTNO; + send_negotiation(data, CURL_WONT, option); + break; + + case CURL_WANTNO: + switch(tn->usq[option]) { + case CURL_EMPTY: + /* Already negotiating for NO */ + break; + case CURL_OPPOSITE: + tn->usq[option] = CURL_EMPTY; + break; + } + break; + + case CURL_WANTYES: + switch(tn->usq[option]) { + case CURL_EMPTY: + tn->usq[option] = CURL_OPPOSITE; + break; + case CURL_OPPOSITE: + break; + } + break; + } + } +} + +static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn) +{ + int i; + + for(i = 0; i < CURL_NTELOPTS; i++) { + if(i == CURL_TELOPT_ECHO) + continue; + + if(tn->us_preferred[i] == CURL_YES) + set_local_option(data, tn, i, CURL_YES); + + if(tn->him_preferred[i] == CURL_YES) + set_remote_option(data, tn, i, CURL_YES); + } +} + static void rec_will(struct Curl_easy *data, struct TELNET *tn, int option) { switch(tn->him[option]) { @@ -486,77 +494,233 @@ static void rec_wont(struct Curl_easy *data, struct TELNET *tn, int option) } } -static void set_local_option(struct Curl_easy *data, struct TELNET *tn, - int option, int newstate) +static void printsub(struct Curl_easy *data, + int direction, /* '<' or '>' */ + const unsigned char *pointer, /* ptr to suboption data */ + size_t length) /* suboption data length */ { - if(newstate == CURL_YES) { - switch(tn->us[option]) { - case CURL_NO: - tn->us[option] = CURL_WANTYES; - send_negotiation(data, CURL_WILL, option); - break; + if(data->set.verbose) { + unsigned int i = 0; + if(direction) { + infof(data, "%s IAC SB ", (direction == '<') ? "RCVD" : "SENT"); + if(length >= 3) { + int j; - case CURL_YES: - /* Already enabled */ - break; + i = pointer[length - 2]; + j = pointer[length - 1]; - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Already negotiating for CURL_YES, queue the request */ - tn->usq[option] = CURL_OPPOSITE; - break; - case CURL_OPPOSITE: - /* Error: already queued an enable request */ - break; + if(i != CURL_IAC || j != CURL_SE) { + infof(data, "(terminated by "); + if(CURL_TELOPT_OK(i)) + infof(data, "%s ", CURL_TELOPT(i)); + else if(CURL_TELCMD_OK(i)) + infof(data, "%s ", CURL_TELCMD(i)); + else + infof(data, "%u ", i); + if(CURL_TELOPT_OK(j)) + infof(data, "%s", CURL_TELOPT(j)); + else if(CURL_TELCMD_OK(j)) + infof(data, "%s", CURL_TELCMD(j)); + else + infof(data, "%d", j); + infof(data, ", not IAC SE) "); + } } - break; + if(length >= 2) + length -= 2; + else /* bad input */ + return; + } + if(length <= 1) { + infof(data, "(Empty suboption?)"); + return; + } - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Error: already negotiating for enable */ + if(CURL_TELOPT_OK(pointer[0])) { + switch(pointer[0]) { + case CURL_TELOPT_TTYPE: + case CURL_TELOPT_XDISPLOC: + case CURL_TELOPT_NEW_ENVIRON: + case CURL_TELOPT_NAWS: + infof(data, "%s", CURL_TELOPT(pointer[0])); break; - case CURL_OPPOSITE: - tn->usq[option] = CURL_EMPTY; + default: + infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); break; } - break; } - } - else { /* NO */ - switch(tn->us[option]) { - case CURL_NO: - /* Already disabled */ - break; + else + infof(data, "%d (unknown)", pointer[0]); - case CURL_YES: - tn->us[option] = CURL_WANTNO; - send_negotiation(data, CURL_WONT, option); + switch(pointer[0]) { + case CURL_TELOPT_NAWS: + if(length > 4) + infof(data, "Width: %d ; Height: %d", (pointer[1] << 8) | pointer[2], + (pointer[3] << 8) | pointer[4]); break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Already negotiating for NO */ + default: + switch(pointer[1]) { + case CURL_TELQUAL_IS: + infof(data, " IS"); break; - case CURL_OPPOSITE: - tn->usq[option] = CURL_EMPTY; + case CURL_TELQUAL_SEND: + infof(data, " SEND"); + break; + case CURL_TELQUAL_INFO: + infof(data, " INFO/REPLY"); + break; + case CURL_TELQUAL_NAME: + infof(data, " NAME"); break; } - break; - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->usq[option] = CURL_OPPOSITE; + switch(pointer[0]) { + case CURL_TELOPT_TTYPE: + case CURL_TELOPT_XDISPLOC: + infof(data, " \"%.*s\"", + (int)((length > 2) ? (length - 2) : 0), &pointer[2]); break; - case CURL_OPPOSITE: + case CURL_TELOPT_NEW_ENVIRON: + if(pointer[1] == CURL_TELQUAL_IS) { + infof(data, " "); + for(i = 3; i < length; i++) { + switch(pointer[i]) { + case CURL_NEW_ENV_VAR: + infof(data, ", "); + break; + case CURL_NEW_ENV_VALUE: + infof(data, " = "); + break; + default: + infof(data, "%c", pointer[i]); + break; + } + } + } + break; + default: + for(i = 2; i < length; i++) + infof(data, " %.2x", pointer[i]); break; } + } + } +} + +/* Escape and send a telnet data block */ +static CURLcode send_telnet_data(struct Curl_easy *data, + struct TELNET *tn, + const char *buffer, ssize_t nread) +{ + size_t i, outlen; + const unsigned char *outbuf; + CURLcode result = CURLE_OK; + size_t bytes_written; + size_t total_written = 0; + struct connectdata *conn = data->conn; + + DEBUGASSERT(tn); + DEBUGASSERT(nread > 0); + if(nread < 0) + return CURLE_TOO_LARGE; + + if(memchr(buffer, CURL_IAC, nread)) { + /* only use the escape buffer when necessary */ + curlx_dyn_reset(&tn->out); + + for(i = 0; i < (size_t)nread && !result; i++) { + result = curlx_dyn_addn(&tn->out, &buffer[i], 1); + if(!result && ((unsigned char)buffer[i] == CURL_IAC)) + /* IAC is FF in hex */ + result = curlx_dyn_addn(&tn->out, "\xff", 1); + } + + outlen = curlx_dyn_len(&tn->out); + outbuf = curlx_dyn_uptr(&tn->out); + } + else { + outlen = (size_t)nread; + outbuf = (const unsigned char *)buffer; + } + while(!result && total_written < outlen) { + /* Make sure socket is writable to avoid EWOULDBLOCK condition */ + struct pollfd pfd[1]; + pfd[0].fd = conn->sock[FIRSTSOCKET]; + pfd[0].events = POLLOUT; + switch(Curl_poll(pfd, 1, -1)) { + case -1: /* error, abort writing */ + case 0: /* timeout (will never happen) */ + result = CURLE_SEND_ERROR; + break; + default: /* write! */ + bytes_written = 0; + result = Curl_xfer_send(data, outbuf + total_written, + outlen - total_written, FALSE, &bytes_written); + total_written += bytes_written; break; } } + + return result; +} + +/* + * sendsuboption() + * + * Send suboption information to the server side. + */ +static void sendsuboption(struct Curl_easy *data, + struct TELNET *tn, int option) +{ + ssize_t bytes_written; + int err; + unsigned short x, y; + const unsigned char *uc1, *uc2; + struct connectdata *conn = data->conn; + + switch(option) { + case CURL_TELOPT_NAWS: + /* We prepare data to be sent */ + CURL_SB_CLEAR(tn); + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SB); + CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS); + /* We must deal either with little or big endian processors */ + /* Window size must be sent according to the 'network order' */ + x = htons(tn->subopt_wsx); + y = htons(tn->subopt_wsy); + uc1 = (const unsigned char *)&x; + uc2 = (const unsigned char *)&y; + CURL_SB_ACCUM(tn, uc1[0]); + CURL_SB_ACCUM(tn, uc1[1]); + CURL_SB_ACCUM(tn, uc2[0]); + CURL_SB_ACCUM(tn, uc2[1]); + + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SE); + CURL_SB_TERM(tn); + /* data suboption is now ready */ + + printsub(data, '>', (const unsigned char *)tn->subbuffer + 2, + CURL_SB_LEN(tn) - 2); + + /* we send the header of the suboption... */ + bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + } + /* ... then the window size with the send_telnet_data() function + to deal with 0xFF cases ... */ + send_telnet_data(data, tn, (const char *)tn->subbuffer + 3, 4); + /* ... and the footer */ + bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + } + break; + } } static void rec_do(struct Curl_easy *data, struct TELNET *tn, int option) @@ -657,119 +821,6 @@ static void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option) } } -static void printsub(struct Curl_easy *data, - int direction, /* '<' or '>' */ - unsigned char *pointer, /* where suboption data is */ - size_t length) /* length of suboption data */ -{ - if(data->set.verbose) { - unsigned int i = 0; - if(direction) { - infof(data, "%s IAC SB ", (direction == '<') ? "RCVD" : "SENT"); - if(length >= 3) { - int j; - - i = pointer[length - 2]; - j = pointer[length - 1]; - - if(i != CURL_IAC || j != CURL_SE) { - infof(data, "(terminated by "); - if(CURL_TELOPT_OK(i)) - infof(data, "%s ", CURL_TELOPT(i)); - else if(CURL_TELCMD_OK(i)) - infof(data, "%s ", CURL_TELCMD(i)); - else - infof(data, "%u ", i); - if(CURL_TELOPT_OK(j)) - infof(data, "%s", CURL_TELOPT(j)); - else if(CURL_TELCMD_OK(j)) - infof(data, "%s", CURL_TELCMD(j)); - else - infof(data, "%d", j); - infof(data, ", not IAC SE) "); - } - } - if(length >= 2) - length -= 2; - else /* bad input */ - return; - } - if(length <= 1) { - infof(data, "(Empty suboption?)"); - return; - } - - if(CURL_TELOPT_OK(pointer[0])) { - switch(pointer[0]) { - case CURL_TELOPT_TTYPE: - case CURL_TELOPT_XDISPLOC: - case CURL_TELOPT_NEW_ENVIRON: - case CURL_TELOPT_NAWS: - infof(data, "%s", CURL_TELOPT(pointer[0])); - break; - default: - infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); - break; - } - } - else - infof(data, "%d (unknown)", pointer[0]); - - switch(pointer[0]) { - case CURL_TELOPT_NAWS: - if(length > 4) - infof(data, "Width: %d ; Height: %d", (pointer[1] << 8) | pointer[2], - (pointer[3] << 8) | pointer[4]); - break; - default: - switch(pointer[1]) { - case CURL_TELQUAL_IS: - infof(data, " IS"); - break; - case CURL_TELQUAL_SEND: - infof(data, " SEND"); - break; - case CURL_TELQUAL_INFO: - infof(data, " INFO/REPLY"); - break; - case CURL_TELQUAL_NAME: - infof(data, " NAME"); - break; - } - - switch(pointer[0]) { - case CURL_TELOPT_TTYPE: - case CURL_TELOPT_XDISPLOC: - infof(data, " \"%.*s\"", - (int)((length > 2) ? (length - 2) : 0), &pointer[2]); - break; - case CURL_TELOPT_NEW_ENVIRON: - if(pointer[1] == CURL_TELQUAL_IS) { - infof(data, " "); - for(i = 3; i < length; i++) { - switch(pointer[i]) { - case CURL_NEW_ENV_VAR: - infof(data, ", "); - break; - case CURL_NEW_ENV_VALUE: - infof(data, " = "); - break; - default: - infof(data, "%c", pointer[i]); - break; - } - } - } - break; - default: - for(i = 2; i < length; i++) - infof(data, " %.2x", pointer[i]); - break; - } - } - } -} - static bool str_is_nonascii(const char *str) { char c; @@ -808,9 +859,9 @@ static CURLcode check_telnet_options(struct Curl_easy *data, for(head = data->set.telnet_options; head && !result; head = head->next) { size_t olen; - char *option = head->data; - char *arg; - char *sep = strchr(option, '='); + const char *option = head->data; + const char *arg; + const char *sep = strchr(option, '='); if(sep) { olen = sep - option; arg = ++sep; @@ -910,7 +961,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data, /* if the option contains an IAC code, it should be escaped in the output, but as we cannot think of any legit way to send that as part of the content we - rather just ban its use instead */ + rather ban its use instead */ static bool bad_option(const char *data) { return !data || !!strchr(data, CURL_IAC); @@ -934,7 +985,8 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) if(!CURL_SB_LEN(tn)) /* ignore empty suboption */ return CURLE_OK; - printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2); + printsub(data, '<', (const unsigned char *)tn->subbuffer, + CURL_SB_LEN(tn) + 2); switch(CURL_SB_GET(tn)) { case CURL_TELOPT_TTYPE: if(bad_option(tn->subopt_ttype)) @@ -985,7 +1037,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) return CURLE_BAD_FUNCTION_ARGUMENT; /* Add the variable if it fits */ if(len + tmplen < (int)sizeof(temp) - 6) { - char *s = strchr(v->data, ','); + const char *s = strchr(v->data, ','); if(!s) len += curl_msnprintf((char *)&temp[len], sizeof(temp) - len, "%c%s", CURL_NEW_ENV_VAR, v->data); @@ -1011,65 +1063,6 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) return CURLE_OK; } -/* - * sendsuboption() - * - * Send suboption information to the server side. - */ -static void sendsuboption(struct Curl_easy *data, - struct TELNET *tn, int option) -{ - ssize_t bytes_written; - int err; - unsigned short x, y; - unsigned char *uc1, *uc2; - struct connectdata *conn = data->conn; - - switch(option) { - case CURL_TELOPT_NAWS: - /* We prepare data to be sent */ - CURL_SB_CLEAR(tn); - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, CURL_SB); - CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS); - /* We must deal either with little or big endian processors */ - /* Window size must be sent according to the 'network order' */ - x = htons(tn->subopt_wsx); - y = htons(tn->subopt_wsy); - uc1 = (unsigned char *)&x; - uc2 = (unsigned char *)&y; - CURL_SB_ACCUM(tn, uc1[0]); - CURL_SB_ACCUM(tn, uc1[1]); - CURL_SB_ACCUM(tn, uc2[0]); - CURL_SB_ACCUM(tn, uc2[1]); - - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, CURL_SE); - CURL_SB_TERM(tn); - /* data suboption is now ready */ - - printsub(data, '>', (unsigned char *)tn->subbuffer + 2, - CURL_SB_LEN(tn) - 2); - - /* we send the header of the suboption... */ - bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } - /* ... then the window size with the send_telnet_data() function - to deal with 0xFF cases ... */ - send_telnet_data(data, tn, (char *)tn->subbuffer + 3, 4); - /* ... and the footer */ - bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } - break; - } -} - static CURLcode telrcv(struct Curl_easy *data, struct TELNET *tn, const unsigned char *inbuf, /* Data received from @@ -1224,63 +1217,6 @@ static CURLcode telrcv(struct Curl_easy *data, return CURLE_OK; } -/* Escape and send a telnet data block */ -static CURLcode send_telnet_data(struct Curl_easy *data, - struct TELNET *tn, - char *buffer, ssize_t nread) -{ - size_t i, outlen; - unsigned char *outbuf; - CURLcode result = CURLE_OK; - size_t bytes_written; - size_t total_written = 0; - struct connectdata *conn = data->conn; - - DEBUGASSERT(tn); - DEBUGASSERT(nread > 0); - if(nread < 0) - return CURLE_TOO_LARGE; - - if(memchr(buffer, CURL_IAC, nread)) { - /* only use the escape buffer when necessary */ - curlx_dyn_reset(&tn->out); - - for(i = 0; i < (size_t)nread && !result; i++) { - result = curlx_dyn_addn(&tn->out, &buffer[i], 1); - if(!result && ((unsigned char)buffer[i] == CURL_IAC)) - /* IAC is FF in hex */ - result = curlx_dyn_addn(&tn->out, "\xff", 1); - } - - outlen = curlx_dyn_len(&tn->out); - outbuf = curlx_dyn_uptr(&tn->out); - } - else { - outlen = (size_t)nread; - outbuf = (unsigned char *)buffer; - } - while(!result && total_written < outlen) { - /* Make sure socket is writable to avoid EWOULDBLOCK condition */ - struct pollfd pfd[1]; - pfd[0].fd = conn->sock[FIRSTSOCKET]; - pfd[0].events = POLLOUT; - switch(Curl_poll(pfd, 1, -1)) { - case -1: /* error, abort writing */ - case 0: /* timeout (will never happen) */ - result = CURLE_SEND_ERROR; - break; - default: /* write! */ - bytes_written = 0; - result = Curl_xfer_send(data, outbuf + total_written, - outlen - total_written, FALSE, &bytes_written); - total_written += bytes_written; - break; - } - } - - return result; -} - static CURLcode telnet_done(struct Curl_easy *data, CURLcode status, bool premature) { @@ -1342,8 +1278,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } /* Tell Winsock what events we want to listen to */ - if(WSAEventSelect(sockfd, event_handle, FD_READ | FD_CLOSE) == - SOCKET_ERROR) { + if(WSAEventSelect(sockfd, event_handle, FD_READ | FD_CLOSE) != 0) { WSACloseEvent(event_handle); return CURLE_RECV_ERROR; } @@ -1358,7 +1293,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it, else use the old WaitForMultipleObjects() way */ if(GetFileType(stdin_handle) == FILE_TYPE_PIPE || data->set.is_fread_set) { - /* Do not wait for stdin_handle, just wait for event_handle */ + /* Do not wait for stdin_handle, wait for event_handle */ obj_count = 1; /* Check stdin_handle per 100 milliseconds */ wait_timeout = 100; @@ -1441,7 +1376,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) case WAIT_OBJECT_0: { events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) { + if(WSAEnumNetworkEvents(sockfd, event_handle, &events) != 0) { err = SOCKERRNO; if(err != SOCKEINPROGRESS) { infof(data, "WSAEnumNetworkEvents failed (%d)", err); @@ -1630,4 +1565,44 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) return result; } + +/* + * TELNET protocol handler. + */ +static const struct Curl_protocol Curl_protocol_telnet = { + ZERO_NULL, /* setup_connection */ + telnet_do, /* do_it */ + telnet_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_pollset */ + ZERO_NULL, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif /* !CURL_DISABLE_TELNET */ + +/* + * TELNET protocol handler. + */ +const struct Curl_scheme Curl_scheme_telnet = { + "telnet", /* scheme */ +#ifdef CURL_DISABLE_TELNET + ZERO_NULL, +#else + &Curl_protocol_telnet, #endif + CURLPROTO_TELNET, /* protocol */ + CURLPROTO_TELNET, /* family */ + PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */ + PORT_TELNET, /* defport */ +}; diff --git a/vendor/curl/lib/telnet.h b/vendor/curl/lib/telnet.h index 30782d8..c106179 100644 --- a/vendor/curl/lib/telnet.h +++ b/vendor/curl/lib/telnet.h @@ -23,8 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef CURL_DISABLE_TELNET -extern const struct Curl_handler Curl_handler_telnet; -#endif +extern const struct Curl_scheme Curl_scheme_telnet; #endif /* HEADER_CURL_TELNET_H */ diff --git a/vendor/curl/lib/tftp.c b/vendor/curl/lib/tftp.c index 69571bb..cc0769b 100644 --- a/vendor/curl/lib/tftp.c +++ b/vendor/curl/lib/tftp.c @@ -22,6 +22,8 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "tftp.h" #ifndef CURL_DISABLE_TFTP @@ -45,13 +47,11 @@ #include #endif -#include "urldata.h" #include "cfilters.h" #include "cf-socket.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" -#include "tftp.h" #include "progress.h" #include "connect.h" #include "sockaddr.h" /* required for Curl_sockaddr_storage */ @@ -142,49 +142,6 @@ struct tftp_conn { BIT(remote_pinned); }; -/* Forward declarations */ -static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event); -static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event); -static CURLcode tftp_connect(struct Curl_easy *data, bool *done); -static CURLcode tftp_do(struct Curl_easy *data, bool *done); -static CURLcode tftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode tftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode tftp_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode tftp_translate_code(tftp_error_t error); - -/* - * TFTP protocol handler. - */ -const struct Curl_handler Curl_handler_tftp = { - "tftp", /* scheme */ - tftp_setup_connection, /* setup_connection */ - tftp_do, /* do_it */ - tftp_done, /* done */ - ZERO_NULL, /* do_more */ - tftp_connect, /* connect_it */ - tftp_multi_statemach, /* connecting */ - tftp_doing, /* doing */ - tftp_pollset, /* proto_pollset */ - tftp_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_TFTP, /* defport */ - CURLPROTO_TFTP, /* protocol */ - CURLPROTO_TFTP, /* family */ - PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */ -}; - /********************************************************** * * tftp_set_timeouts - @@ -199,10 +156,9 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state) { time_t timeout; timediff_t timeout_ms; - bool start = (state->state == TFTP_STATE_START); /* Compute drop-dead time */ - timeout_ms = Curl_timeleft_ms(state->data, start); + timeout_ms = Curl_timeleft_ms(state->data); if(timeout_ms < 0) { /* time-out, bail out, go home */ @@ -383,185 +339,173 @@ static CURLcode tftp_option_add(struct tftp_conn *state, size_t *csize, return CURLE_OK; } -static CURLcode tftp_connect_for_tx(struct tftp_conn *state, - tftp_event_t event) -{ - CURLcode result; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - struct Curl_easy *data = state->data; - - infof(data, "%s", "Connected for transmit"); -#endif - state->state = TFTP_STATE_TX; - result = tftp_set_timeouts(state); - if(result) - return result; - return tftp_tx(state, event); -} - -static CURLcode tftp_connect_for_rx(struct tftp_conn *state, - tftp_event_t event) -{ - CURLcode result; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - struct Curl_easy *data = state->data; - - infof(data, "%s", "Connected for receive"); -#endif - state->state = TFTP_STATE_RX; - result = tftp_set_timeouts(state); - if(result) - return result; - return tftp_rx(state, event); -} +/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16-bit + boundary */ +#define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff) -static CURLcode tftp_send_first(struct tftp_conn *state, - tftp_event_t event) +/********************************************************** + * + * tftp_tx + * + * Event handler for the TX state + * + **********************************************************/ +static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) { - size_t sbytes; - ssize_t senddata; - const char *mode = "octet"; - char *filename; struct Curl_easy *data = state->data; - const struct Curl_sockaddr_ex *remote_addr = NULL; + ssize_t sbytes; CURLcode result = CURLE_OK; - - /* Set ASCII mode if -B flag was used */ - if(data->state.prefer_ascii) - mode = "netascii"; + struct SingleRequest *k = &data->req; + size_t cb; /* Bytes currently read */ + char buffer[STRERROR_LEN]; + char *bufptr; + bool eos; switch(event) { - case TFTP_EVENT_INIT: /* Send the first packet out */ - case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ - /* Increment the retry counter, quit if over the limit */ - state->retries++; - if(state->retries > state->retry_max) { - state->error = TFTP_ERR_NORESPONSE; - state->state = TFTP_STATE_FIN; - return result; - } + case TFTP_EVENT_ACK: + case TFTP_EVENT_OACK: + if(event == TFTP_EVENT_ACK) { + /* Ack the packet */ + int rblock = getrpacketblock(&state->rpacket); - if(data->state.upload) { - /* If we are uploading, send an WRQ */ - setpacketevent(&state->spacket, TFTP_EVENT_WRQ); - if(data->state.infilesize != -1) - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - else { - /* If we are downloading, send an RRQ */ - setpacketevent(&state->spacket, TFTP_EVENT_RRQ); - } - /* As RFC3617 describes the separator slash is not actually part of the - filename so we skip the always-present first letter of the path - string. */ - if(!state->data->state.up.path[1]) { - failf(data, "Missing filename"); - return CURLE_TFTP_ILLEGAL; - } - result = Curl_urldecode(&state->data->state.up.path[1], 0, - &filename, NULL, REJECT_ZERO); - if(result) - return result; + if(rblock != state->block && + /* There is a bug in tftpd-hpa that causes it to send us an ack for + * 65535 when the block number wraps to 0. So when we are expecting + * 0, also accept 65535. See + * https://www.syslinux.org/archives/2010-September/015612.html + * */ + !(state->block == 0 && rblock == 65535)) { + /* This is not the expected block. Log it and up the retry counter */ + infof(data, "Received ACK for block %d, expecting %d", + rblock, state->block); + state->retries++; + /* Bail out if over the maximum */ + if(state->retries > state->retry_max) { + failf(data, "tftp_tx: giving up waiting for block %d ack", + state->block); + result = CURLE_SEND_ERROR; + } + else { + /* Re-send the data packet */ + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes < 0) { + failf(data, "%s", + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + result = CURLE_SEND_ERROR; + } + } - if(strlen(filename) > (state->blksize - strlen(mode) - 4)) { - failf(data, "TFTP filename too long"); - curlx_free(filename); - return CURLE_TFTP_ILLEGAL; /* too long filename field */ + return result; + } + /* This is the expected packet. Reset the counters and send the next + block */ + state->rx_time = time(NULL); + state->block++; } + else + state->block = 1; /* first data block is 1 when using OACK */ - curl_msnprintf((char *)state->spacket.data + 2, - state->blksize, - "%s%c%s%c", filename, '\0', mode, '\0'); - sbytes = 4 + strlen(filename) + strlen(mode); - curlx_free(filename); - - /* optional addition of TFTP options */ - if(!data->set.tftp_no_options) { - char buf[64]; - /* add tsize option */ - curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, - data->state.upload && (data->state.infilesize != -1) ? - data->state.infilesize : 0); - - result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, buf); - - /* add blksize option */ - curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, buf); - - /* add timeout option */ - curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, sbytes, buf); - - if(result != CURLE_OK) { - failf(data, "TFTP buffer too small for options"); - return CURLE_TFTP_ILLEGAL; - } + state->retries = 0; + setpacketevent(&state->spacket, TFTP_EVENT_DATA); + setpacketblock(&state->spacket, state->block); + if(state->block > 1 && state->sbytes < state->blksize) { + state->state = TFTP_STATE_FIN; + return CURLE_OK; } - /* the typecase for the 3rd argument is mostly for systems that do - not have a size_t argument, like older unixes that want an 'int' */ -#ifdef __AMIGA__ -#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) -#else -#define CURL_SENDTO_ARG5(x) (x) -#endif - remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET); - if(!remote_addr) - return CURLE_FAILED_INIT; + /* TFTP considers data block size < 512 bytes as an end of session. So + * in some cases we must wait for additional data to build full (512 bytes) + * data block. + * */ + state->sbytes = 0; + bufptr = (char *)state->spacket.data + 4; + do { + result = Curl_client_read(data, bufptr, state->blksize - state->sbytes, + &cb, &eos); + if(result) + return result; + state->sbytes += cb; + bufptr += cb; + } while(state->sbytes < state->blksize && cb); - senddata = sendto(state->sockfd, (void *)state->spacket.data, - (SEND_TYPE_ARG3)sbytes, 0, - CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr), - (curl_socklen_t)remote_addr->addrlen); - if(senddata != (ssize_t)sbytes) { - char buffer[STRERROR_LEN]; + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes < 0) { failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); return CURLE_SEND_ERROR; } + /* Update the progress meter */ + k->writebytecount += state->sbytes; + Curl_pgrs_upload_inc(data, state->sbytes); break; - case TFTP_EVENT_OACK: - if(data->state.upload) { - result = tftp_connect_for_tx(state, event); + case TFTP_EVENT_TIMEOUT: + /* Increment the retry counter and log the timeout */ + state->retries++; + infof(data, "Timeout waiting for block %d ACK. " + "Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); + /* Decide if we have had enough */ + if(state->retries > state->retry_max) { + state->error = TFTP_ERR_TIMEOUT; + state->state = TFTP_STATE_FIN; } else { - result = tftp_connect_for_rx(state, event); + /* Re-send the data packet */ + sbytes = sendto(state->sockfd, (void *)state->spacket.data, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* Check all sbytes were sent */ + if(sbytes < 0) { + failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + /* since this was a re-send, we remain at the still byte position */ + Curl_pgrsSetUploadCounter(data, k->writebytecount); } break; - case TFTP_EVENT_ACK: /* Connected for transmit */ - result = tftp_connect_for_tx(state, event); - break; - - case TFTP_EVENT_DATA: /* Connected for receive */ - result = tftp_connect_for_rx(state, event); - break; - case TFTP_EVENT_ERROR: state->state = TFTP_STATE_FIN; + setpacketevent(&state->spacket, TFTP_EVENT_ERROR); + setpacketblock(&state->spacket, state->block); + (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + /* do not bother with the return code, but if the socket is still up we + * should be a good TFTP client and let the server know we are done */ + state->state = TFTP_STATE_FIN; break; default: - failf(state->data, "tftp_send_first: internal error"); - return CURLE_TFTP_ILLEGAL; + failf(data, "tftp_tx: internal error, event: %i", (int)event); + break; } return result; } -/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit - boundary */ -#define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff) +static CURLcode tftp_connect_for_tx(struct tftp_conn *state, + tftp_event_t event) +{ + CURLcode result; + + infof(state->data, "%s", "Connected for transmit"); + + state->state = TFTP_STATE_TX; + result = tftp_set_timeouts(state); + if(result) + return result; + return tftp_tx(state, event); +} /********************************************************** * @@ -592,7 +536,7 @@ static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event) infof(data, "Received last DATA packet block %d again.", rblock); } else { - /* totally unexpected, just log it */ + /* totally unexpected, log it */ infof(data, "Received unexpected DATA packet block %d, expecting block %d", rblock, NEXT_BLOCKNUM(state->block)); @@ -604,222 +548,72 @@ static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event) setpacketevent(&state->spacket, TFTP_EVENT_ACK); setpacketblock(&state->spacket, state->block); sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - if(sbytes < 0) { - failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - - /* Check if completed (That is, a less than full packet is received) */ - if(state->rbytes < (ssize_t)state->blksize + 4) { - state->state = TFTP_STATE_FIN; - } - else { - state->state = TFTP_STATE_RX; - } - state->rx_time = time(NULL); - break; - - case TFTP_EVENT_OACK: - /* ACK option acknowledgement so we can move on to data */ - state->block = 0; - state->retries = 0; - setpacketevent(&state->spacket, TFTP_EVENT_ACK); - setpacketblock(&state->spacket, state->block); - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - if(sbytes < 0) { - failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - - /* we are ready to RX data */ - state->state = TFTP_STATE_RX; - state->rx_time = time(NULL); - break; - - case TFTP_EVENT_TIMEOUT: - /* Increment the retry count and fail if over the limit */ - state->retries++; - infof(data, - "Timeout waiting for block %d ACK. Retries = %d", - NEXT_BLOCKNUM(state->block), state->retries); - if(state->retries > state->retry_max) { - state->error = TFTP_ERR_TIMEOUT; - state->state = TFTP_STATE_FIN; - } - else { - /* Resend the previous ACK */ - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - if(sbytes < 0) { - failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - } - break; - - case TFTP_EVENT_ERROR: - setpacketevent(&state->spacket, TFTP_EVENT_ERROR); - setpacketblock(&state->spacket, state->block); - (void)sendto(state->sockfd, (void *)state->spacket.data, - 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* do not bother with the return code, but if the socket is still up we - * should be a good TFTP client and let the server know we are done */ - state->state = TFTP_STATE_FIN; - break; - - default: - failf(data, "%s", "tftp_rx: internal error"); - return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for - this */ - } - return CURLE_OK; -} - -/********************************************************** - * - * tftp_tx - * - * Event handler for the TX state - * - **********************************************************/ -static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) -{ - struct Curl_easy *data = state->data; - ssize_t sbytes; - CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - size_t cb; /* Bytes currently read */ - char buffer[STRERROR_LEN]; - char *bufptr; - bool eos; - - switch(event) { - - case TFTP_EVENT_ACK: - case TFTP_EVENT_OACK: - if(event == TFTP_EVENT_ACK) { - /* Ack the packet */ - int rblock = getrpacketblock(&state->rpacket); - - if(rblock != state->block && - /* There is a bug in tftpd-hpa that causes it to send us an ack for - * 65535 when the block number wraps to 0. So when we are expecting - * 0, also accept 65535. See - * https://www.syslinux.org/archives/2010-September/015612.html - * */ - !(state->block == 0 && rblock == 65535)) { - /* This is not the expected block. Log it and up the retry counter */ - infof(data, "Received ACK for block %d, expecting %d", - rblock, state->block); - state->retries++; - /* Bail out if over the maximum */ - if(state->retries > state->retry_max) { - failf(data, "tftp_tx: giving up waiting for block %d ack", - state->block); - result = CURLE_SEND_ERROR; - } - else { - /* Re-send the data packet */ - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* Check all sbytes were sent */ - if(sbytes < 0) { - failf(data, "%s", - curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); - result = CURLE_SEND_ERROR; - } - } - - return result; - } - /* This is the expected packet. Reset the counters and send the next - block */ - state->rx_time = time(NULL); - state->block++; + 4, SEND_4TH_ARG, + (struct sockaddr *)&state->remote_addr, + state->remote_addrlen); + if(sbytes < 0) { + failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; } - else - state->block = 1; /* first data block is 1 when using OACK */ - state->retries = 0; - setpacketevent(&state->spacket, TFTP_EVENT_DATA); - setpacketblock(&state->spacket, state->block); - if(state->block > 1 && state->sbytes < state->blksize) { + /* Check if completed (That is, a less than full packet is received) */ + if(state->rbytes < (ssize_t)state->blksize + 4) { state->state = TFTP_STATE_FIN; - return CURLE_OK; } + else { + state->state = TFTP_STATE_RX; + } + state->rx_time = time(NULL); + break; - /* TFTP considers data block size < 512 bytes as an end of session. So - * in some cases we must wait for additional data to build full (512 bytes) - * data block. - * */ - state->sbytes = 0; - bufptr = (char *)state->spacket.data + 4; - do { - result = Curl_client_read(data, bufptr, state->blksize - state->sbytes, - &cb, &eos); - if(result) - return result; - state->sbytes += cb; - bufptr += cb; - } while(state->sbytes < state->blksize && cb); - + case TFTP_EVENT_OACK: + /* ACK option acknowledgement so we can move on to data */ + state->block = 0; + state->retries = 0; + setpacketevent(&state->spacket, TFTP_EVENT_ACK); + setpacketblock(&state->spacket, state->block); sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + 4, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); - /* Check all sbytes were sent */ if(sbytes < 0) { failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); return CURLE_SEND_ERROR; } - /* Update the progress meter */ - k->writebytecount += state->sbytes; - Curl_pgrs_upload_inc(data, state->sbytes); + + /* we are ready to RX data */ + state->state = TFTP_STATE_RX; + state->rx_time = time(NULL); break; case TFTP_EVENT_TIMEOUT: - /* Increment the retry counter and log the timeout */ + /* Increment the retry count and fail if over the limit */ state->retries++; - infof(data, "Timeout waiting for block %d ACK. " - " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); - /* Decide if we have had enough */ + infof(data, + "Timeout waiting for block %d ACK. Retries = %d", + NEXT_BLOCKNUM(state->block), state->retries); if(state->retries > state->retry_max) { state->error = TFTP_ERR_TIMEOUT; state->state = TFTP_STATE_FIN; } else { - /* Re-send the data packet */ + /* Resend the previous ACK */ sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, + 4, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); - /* Check all sbytes were sent */ if(sbytes < 0) { failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); return CURLE_SEND_ERROR; } - /* since this was a re-send, we remain at the still byte position */ - Curl_pgrsSetUploadCounter(data, k->writebytecount); } break; case TFTP_EVENT_ERROR: - state->state = TFTP_STATE_FIN; setpacketevent(&state->spacket, TFTP_EVENT_ERROR); setpacketblock(&state->spacket, state->block); - (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, + (void)sendto(state->sockfd, (void *)state->spacket.data, + 4, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); /* do not bother with the return code, but if the socket is still up we @@ -828,8 +622,166 @@ static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) break; default: - failf(data, "tftp_tx: internal error, event: %i", (int)(event)); + failf(data, "%s", "tftp_rx: internal error"); + return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for + this */ + } + return CURLE_OK; +} + +static CURLcode tftp_connect_for_rx(struct tftp_conn *state, + tftp_event_t event) +{ + CURLcode result; + + infof(state->data, "%s", "Connected for receive"); + + state->state = TFTP_STATE_RX; + result = tftp_set_timeouts(state); + if(result) + return result; + return tftp_rx(state, event); +} + +static CURLcode tftp_send_first(struct tftp_conn *state, + tftp_event_t event) +{ + size_t sbytes; + ssize_t senddata; + const char *mode = "octet"; + char *filename; + struct Curl_easy *data = state->data; + const struct Curl_sockaddr_ex *remote_addr = NULL; + CURLcode result = CURLE_OK; + + /* Set ASCII mode if -B flag was used */ + if(data->state.prefer_ascii) + mode = "netascii"; + + switch(event) { + + case TFTP_EVENT_INIT: /* Send the first packet out */ + case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ + /* Increment the retry counter, quit if over the limit */ + state->retries++; + if(state->retries > state->retry_max) { + state->error = TFTP_ERR_NORESPONSE; + state->state = TFTP_STATE_FIN; + return result; + } + + if(data->state.upload) { + /* If we are uploading, send an WRQ */ + setpacketevent(&state->spacket, TFTP_EVENT_WRQ); + if(data->state.infilesize != -1) + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + else { + /* If we are downloading, send an RRQ */ + setpacketevent(&state->spacket, TFTP_EVENT_RRQ); + } + /* As RFC3617 describes the separator slash is not actually part of the + filename so we skip the always-present first letter of the path + string. */ + if(!state->data->state.up.path[1]) { + failf(data, "Missing filename"); + return CURLE_TFTP_ILLEGAL; + } + result = Curl_urldecode(&state->data->state.up.path[1], 0, + &filename, NULL, REJECT_ZERO); + if(result) + return result; + + if(strlen(filename) + strlen(mode) + 4 > state->blksize) { + failf(data, "TFTP filename too long"); + curlx_free(filename); + return CURLE_TFTP_ILLEGAL; /* too long filename field */ + } + + sbytes = 2 + + curl_msnprintf((char *)state->spacket.data + 2, + state->blksize, + "%s%c%s%c", filename, '\0', mode, '\0'); + curlx_free(filename); + + /* optional addition of TFTP options */ + if(!data->set.tftp_no_options) { + char buf[64]; + /* add tsize option */ + curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, + data->state.upload && (data->state.infilesize != -1) ? + data->state.infilesize : 0); + + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, buf); + + /* add blksize option */ + curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, buf); + + /* add timeout option */ + curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, sbytes, buf); + + if(result != CURLE_OK) { + failf(data, "TFTP buffer too small for options"); + return CURLE_TFTP_ILLEGAL; + } + } + + /* the typecase for the 3rd argument is mostly for systems that do + not have a size_t argument, like older unixes that want an 'int' */ +#ifdef __AMIGA__ +#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) +#else +#define CURL_SENDTO_ARG5(x) (x) +#endif + remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET); + if(!remote_addr) + return CURLE_FAILED_INIT; + + senddata = sendto(state->sockfd, (void *)state->spacket.data, + (SEND_TYPE_ARG3)sbytes, 0, + CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr), + (curl_socklen_t)remote_addr->addrlen); + if(senddata != (ssize_t)sbytes) { + char buffer[STRERROR_LEN]; + failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + break; + + case TFTP_EVENT_OACK: + if(data->state.upload) { + result = tftp_connect_for_tx(state, event); + } + else { + result = tftp_connect_for_rx(state, event); + } + break; + + case TFTP_EVENT_ACK: /* Connected for transmit */ + result = tftp_connect_for_tx(state, event); + break; + + case TFTP_EVENT_DATA: /* Connected for receive */ + result = tftp_connect_for_rx(state, event); + break; + + case TFTP_EVENT_ERROR: + state->state = TFTP_STATE_FIN; break; + + default: + failf(state->data, "tftp_send_first: internal error"); + return CURLE_TFTP_ILLEGAL; } return result; @@ -983,8 +935,8 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; } - /* we do not keep TFTP connections up basically because there is none or - * little gain for UDP */ + /* we do not keep TFTP connections up because there is none or little gain + * for UDP */ connclose(conn, "TFTP"); state->data = data; @@ -1126,7 +1078,7 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data, if(state->rbytes > 4 && (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) { result = Curl_client_write(data, CLIENTWRITE_BODY, - (char *)state->rpacket.data + 4, + (const char *)state->rpacket.data + 4, state->rbytes - 4); if(result) { tftp_state_machine(state, TFTP_EVENT_ERROR); @@ -1136,7 +1088,7 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data, break; case TFTP_EVENT_ERROR: { unsigned short error = getrpacketblock(&state->rpacket); - char *str = (char *)state->rpacket.data + 4; + const char *str = (const char *)state->rpacket.data + 4; size_t strn = state->rbytes - 4; state->error = (tftp_error_t)error; if(tftp_strnlen(str, strn) < strn) @@ -1185,8 +1137,7 @@ static timediff_t tftp_state_timeout(struct tftp_conn *state, if(event) *event = TFTP_EVENT_NONE; - timeout_ms = Curl_timeleft_ms(state->data, - (state->state == TFTP_STATE_START)); + timeout_ms = Curl_timeleft_ms(state->data); if(timeout_ms < 0) { state->error = TFTP_ERR_TIMEOUT; state->state = TFTP_STATE_FIN; @@ -1377,4 +1328,44 @@ static CURLcode tftp_setup_connection(struct Curl_easy *data, return CURLE_OK; } + +/* + * TFTP protocol handler. + */ +static const struct Curl_protocol Curl_protocol_tftp = { + tftp_setup_connection, /* setup_connection */ + tftp_do, /* do_it */ + tftp_done, /* done */ + ZERO_NULL, /* do_more */ + tftp_connect, /* connect_it */ + tftp_multi_statemach, /* connecting */ + tftp_doing, /* doing */ + tftp_pollset, /* proto_pollset */ + tftp_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ZERO_NULL, /* perform_pollset */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif + +/* + * TFTP protocol handler. + */ +const struct Curl_scheme Curl_scheme_tftp = { + "tftp", /* scheme */ +#ifdef CURL_DISABLE_TFTP + ZERO_NULL, +#else + &Curl_protocol_tftp, #endif + CURLPROTO_TFTP, /* protocol */ + CURLPROTO_TFTP, /* family */ + PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY, /* flags */ + PORT_TFTP, /* defport */ +}; diff --git a/vendor/curl/lib/tftp.h b/vendor/curl/lib/tftp.h index 12404bf..91ecd29 100644 --- a/vendor/curl/lib/tftp.h +++ b/vendor/curl/lib/tftp.h @@ -23,11 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef CURL_DISABLE_TFTP -extern const struct Curl_handler Curl_handler_tftp; +extern const struct Curl_scheme Curl_scheme_tftp; #define TFTP_BLKSIZE_MIN 8 #define TFTP_BLKSIZE_MAX 65464 -#endif #endif /* HEADER_CURL_TFTP_H */ diff --git a/vendor/curl/lib/transfer.c b/vendor/curl/lib/transfer.c index fed6a17..6dd2f52 100644 --- a/vendor/curl/lib/transfer.c +++ b/vendor/curl/lib/transfer.c @@ -68,7 +68,6 @@ #include "getinfo.h" #include "multiif.h" #include "connect.h" -#include "mime.h" #include "hsts.h" #include "setopt.h" #include "headers.h" @@ -104,13 +103,13 @@ static int data_pending(struct Curl_easy *data, bool rcvd_eagain) { struct connectdata *conn = data->conn; - if(conn->handler->protocol & PROTO_FAMILY_FTP) + if(conn->scheme->protocol & PROTO_FAMILY_FTP) return Curl_conn_data_pending(data, SECONDARYSOCKET); /* in the case of libssh2, we can never be really sure that we have emptied its internal buffers so we MUST always try until we get EAGAIN back */ return (!rcvd_eagain && - conn->handler->protocol & (CURLPROTO_SCP | CURLPROTO_SFTP)) || + conn->scheme->protocol & (CURLPROTO_SCP | CURLPROTO_SFTP)) || Curl_conn_data_pending(data, FIRSTSOCKET); } @@ -127,16 +126,14 @@ bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc) case CURL_TIMECOND_IFMODSINCE: default: if(timeofdoc <= data->set.timevalue) { - infof(data, - "The requested document is not new enough"); + infof(data, "The requested document is not new enough"); data->info.timecond = TRUE; return FALSE; } break; case CURL_TIMECOND_IFUNMODSINCE: if(timeofdoc >= data->set.timevalue) { - infof(data, - "The requested document is not old enough"); + infof(data, "The requested document is not old enough"); data->info.timecond = TRUE; return FALSE; } @@ -254,12 +251,13 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, if(bytestoread && Curl_rlimit_active(&data->progress.dl.rlimit)) { curl_off_t dl_avail = Curl_rlimit_avail(&data->progress.dl.rlimit, Curl_pgrs_now(data)); - /* DEBUGF(infof(data, "dl_rlimit, available=%" FMT_OFF_T, dl_avail)); - */ - /* In case of rate limited downloads: if this loop already got - * data and less than 16k is left in the limit, break out. - * We want to stutter a bit to keep in the limit, but too small - * receives will just cost cpu unnecessarily. */ +#if 0 + DEBUGF(infof(data, "dl_rlimit, available=%" FMT_OFF_T, dl_avail)); +#endif + /* In case of rate limited downloads: if this loop already got data and + * less than 16k is left in the limit, break out. We want to stutter a + * bit to keep in the limit, but too small receives will cost cpu + * unnecessarily. */ if(dl_avail <= 0) { rate_limited = TRUE; break; @@ -278,7 +276,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, if(data->req.download_done && data->req.no_body && !data->req.resp_trailer) { DEBUGF(infof(data, "EAGAIN, download done, no trailer announced, " - "not waiting for EOS")); + "not waiting for EOS")); blen = 0; /* continue as if we received the EOS */ } @@ -386,7 +384,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) goto out; if(k->keepon) { - if(Curl_timeleft_ms(data, FALSE) < 0) { + if(Curl_timeleft_ms(data) < 0) { if(k->size != -1) { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " out of %" @@ -408,7 +406,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) } else { /* - * The transfer has been performed. Just make some general checks before + * The transfer has been performed. Make some general checks before * returning. */ if(!(data->req.no_body) && (k->size != -1) && @@ -534,7 +532,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) * different ports! */ data->state.allow_port = TRUE; -#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(MSG_NOSIGNAL) /************************************************************* * Tell signal handler to ignore SIGPIPE *************************************************************/ @@ -576,8 +574,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) /* * Set user-agent. Used for HTTP, but since we can attempt to tunnel - * basically anything through an HTTP proxy we cannot limit this based on - * protocol. + * anything through an HTTP proxy we cannot limit this based on protocol. */ if(!result && data->set.str[STRING_USERAGENT]) { curlx_free(data->state.aptr.uagent); @@ -623,13 +620,13 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) protocol is HTTP as when uploading over HTTP we will still get a response */ if(data->state.upload && - !(conn->handler->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_RTSP))) + !(conn->scheme->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_RTSP))) return CURLE_OK; if(conn->bits.reuse && (data->req.bytecount + data->req.headerbytecount == 0) && ((!data->req.no_body && !data->req.done) || - (conn->handler->protocol & PROTO_FAMILY_HTTP)) + (conn->scheme->protocol & PROTO_FAMILY_HTTP)) #ifndef CURL_DISABLE_RTSP && (data->set.rtspreq != RTSPREQ_RECEIVE) #endif @@ -668,11 +665,10 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) return CURLE_OUT_OF_MEMORY; connclose(conn, "retry"); /* close this connection */ - conn->bits.retry = TRUE; /* mark this as a connection we are about - to retry. Marking it this way should - prevent i.e HTTP transfers to return - error just because nothing has been - transferred! */ + conn->bits.retry = TRUE; /* mark this as a connection we are about to + retry. Marking it this way should prevent i.e + HTTP transfers to return error because nothing + has been transferred! */ Curl_creader_set_rewind(data, TRUE); } return CURLE_OK; @@ -701,19 +697,19 @@ static void xfer_setup( /* without receiving, there should be not recv_size */ DEBUGASSERT((conn->recv_idx >= 0) || (recv_size == -1)); k->size = recv_size; - k->header = !!conn->handler->write_resp_hd; + k->header = !!conn->scheme->run->write_resp_hd; /* by default, we do not shutdown at the end of the transfer */ k->shutdown = FALSE; k->shutdown_err_ignore = FALSE; - /* The code sequence below is placed in this function just because all - necessary input is not always known in do_complete() as this function may - be called after that */ + /* The code sequence below is placed in this function because all necessary + input is not always known in do_complete() as this function may be called + after that */ if(!k->header && (recv_size > 0)) Curl_pgrsSetDownloadSize(data, recv_size); /* we want header and/or body, if neither then do not do this! */ - if(conn->handler->write_resp_hd || !data->req.no_body) { + if(conn->scheme->run->write_resp_hd || !data->req.no_body) { if(conn->recv_idx != -1) k->keepon |= KEEP_RECV; @@ -768,10 +764,10 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data, { CURLcode result = CURLE_OK; - if(data->conn->handler->write_resp) { + if(data->conn->scheme->run->write_resp) { /* protocol handlers offering this function take full responsibility * for writing all received download data to the client. */ - result = data->conn->handler->write_resp(data, buf, blen, is_eos); + result = data->conn->scheme->run->write_resp(data, buf, blen, is_eos); } else { /* No special handling by protocol handler, write all received data @@ -802,11 +798,11 @@ bool Curl_xfer_write_is_paused(struct Curl_easy *data) CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data, const char *hd0, size_t hdlen, bool is_eos) { - if(data->conn->handler->write_resp_hd) { + if(data->conn->scheme->run->write_resp_hd) { DEBUGASSERT(!hd0[hdlen]); /* null terminated */ /* protocol handlers offering this function take full responsibility * for writing all received download data to the client. */ - return data->conn->handler->write_resp_hd(data, hd0, hdlen, is_eos); + return data->conn->scheme->run->write_resp_hd(data, hd0, hdlen, is_eos); } /* No special handling by protocol handler, write as response bytes */ return Curl_xfer_write_resp(data, hd0, hdlen, is_eos); @@ -872,8 +868,8 @@ CURLcode Curl_xfer_send_close(struct Curl_easy *data) bool Curl_xfer_is_blocked(struct Curl_easy *data) { - bool want_send = ((data)->req.keepon & KEEP_SEND); - bool want_recv = ((data)->req.keepon & KEEP_RECV); + bool want_send = (data->req.keepon & KEEP_SEND); + bool want_recv = (data->req.keepon & KEEP_RECV); if(!want_send) return want_recv && Curl_xfer_recv_is_paused(data); else if(!want_recv) diff --git a/vendor/curl/lib/uint-hash.c b/vendor/curl/lib/uint-hash.c index e23d249..2377840 100644 --- a/vendor/curl/lib/uint-hash.c +++ b/vendor/curl/lib/uint-hash.c @@ -107,7 +107,7 @@ static void uint32_hash_elem_link(struct uint_hash *h, ++h->size; } -#define CURL_UINT32_HASH_SLOT(h, id) h->table[uint32_hash_hash(id, h->slots)] +#define CURL_UINT32_HASH_SLOT(h, id) h->table[uint32_hash_hash(id, (h)->slots)] #define CURL_UINT32_HASH_SLOT_ADDR(h, id) &CURL_UINT32_HASH_SLOT(h, id) bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value) diff --git a/vendor/curl/lib/uint-table.h b/vendor/curl/lib/uint-table.h index 3fc7e34..398a26a 100644 --- a/vendor/curl/lib/uint-table.h +++ b/vendor/curl/lib/uint-table.h @@ -47,7 +47,7 @@ void Curl_uint32_tbl_init(struct uint32_tbl *tbl, /* Resize the table to change capacity `nmax`. When `nmax` is reduced, * all present entries with key equal or larger to `nmax` are removed. */ -CURLcode Curl_uint32_tbl_resize(struct uint32_tbl *tbl, uint32_t nmax); +CURLcode Curl_uint32_tbl_resize(struct uint32_tbl *tbl, uint32_t nrows); /* Destroy the table, freeing all entries. */ void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl); diff --git a/vendor/curl/lib/url.c b/vendor/curl/lib/url.c index 130be19..ec0457b 100644 --- a/vendor/curl/lib/url.c +++ b/vendor/curl/lib/url.c @@ -58,21 +58,21 @@ #error "We cannot compile without socket() support!" #endif -#if defined(HAVE_IF_NAMETOINDEX) && defined(_WIN32) +#if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK) #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 5) #include /* workaround for old mingw-w64 missing to include it */ #endif #include #endif -#include "doh.h" #include "urldata.h" -#include "formdata.h" #include "mime.h" #include "bufref.h" #include "vtls/vtls.h" +#include "vssh/vssh.h" #include "hostip.h" #include "transfer.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "progress.h" #include "cookie.h" @@ -92,7 +92,7 @@ #include "http_proxy.h" #include "conncache.h" #include "multihandle.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "setopt.h" #include "altsvc.h" #include "curlx/dynbuf.h" @@ -125,15 +125,15 @@ static void data_priority_cleanup(struct Curl_easy *data); #define data_priority_cleanup(x) #endif -/* Some parts of the code (e.g. chunked encoding) assume this buffer has at - * more than just a few bytes to play with. Do not let it become too small or - * bad things will happen. +/* Some parts of the code (e.g. chunked encoding) assume this buffer has more + * than a few bytes to play with. Do not let it become too small or bad things + * will happen. */ #if READBUFFER_SIZE < READBUFFER_MIN # error READBUFFER_SIZE is too small #endif -#ifdef USE_UNIX_SOCKETS +#if !defined(CURL_DISABLE_PROXY) && defined(USE_UNIX_SOCKETS) #define UNIX_SOCKET_PREFIX "localhost" #endif @@ -147,15 +147,15 @@ static void data_priority_cleanup(struct Curl_easy *data); * * Parameters: * - * 'h' [in] - struct Curl_handler pointer. + * 's' [in] - struct Curl_scheme pointer. * * Returns the family as a single bit protocol identifier. */ -static curl_prot_t get_protocol_family(const struct Curl_handler *h) +static curl_prot_t get_protocol_family(const struct Curl_scheme *s) { - DEBUGASSERT(h); - DEBUGASSERT(h->family); - return h->family; + DEBUGASSERT(s); + DEBUGASSERT(s->family); + return s->family; } void Curl_freeset(struct Curl_easy *data) @@ -175,7 +175,10 @@ void Curl_freeset(struct Curl_easy *data) Curl_bufref_free(&data->state.referer); Curl_bufref_free(&data->state.url); - Curl_mime_cleanpart(&data->set.mimepost); +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + Curl_mime_cleanpart(data->set.mimepostp); + Curl_safefree(data->set.mimepostp); +#endif #ifndef CURL_DISABLE_COOKIES curl_slist_free_all(data->state.cookielist); @@ -251,7 +254,7 @@ CURLcode Curl_close(struct Curl_easy **datap) * handle might check the magic and so might any * DEBUGFUNCTION invoked for tracing */ - /* freed here just in case DONE was not called */ + /* freed here in case DONE was not called */ Curl_req_free(&data->req, data); /* Close down all open SSL info and sessions */ @@ -385,8 +388,6 @@ void Curl_init_userdefined(struct Curl_easy *data) set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; #endif - Curl_mime_initpart(&set->mimepost); - Curl_ssl_easy_config_init(data); #ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; @@ -399,7 +400,7 @@ void Curl_init_userdefined(struct Curl_easy *data) #endif set->new_file_perms = 0644; /* Default permissions */ - set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; + set->allowed_protocols = (curl_prot_t) CURLPROTO_64ALL; set->redir_protocols = CURLPROTO_REDIR; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -420,8 +421,8 @@ void Curl_init_userdefined(struct Curl_easy *data) #endif #ifndef CURL_DISABLE_FTP set->wildcard_enabled = FALSE; - set->chunk_bgn = ZERO_NULL; - set->chunk_end = ZERO_NULL; + set->chunk_bgn = ZERO_NULL; + set->chunk_end = ZERO_NULL; set->fnmatch = ZERO_NULL; #endif set->tcp_keepalive = FALSE; @@ -517,9 +518,9 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) DEBUGASSERT(conn); - if(conn->handler && conn->handler->disconnect && + if(conn->scheme && conn->scheme->run->disconnect && !conn->bits.shutdown_handler) - conn->handler->disconnect(data, conn, TRUE); + conn->scheme->run->disconnect(data, conn, TRUE); for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) { Curl_conn_cf_discard_all(data, conn, (int)i); @@ -544,7 +545,6 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->oauth_bearer); Curl_safefree(conn->host.rawalloc); /* hostname buffer */ Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ - Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); Curl_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); @@ -569,7 +569,7 @@ static bool xfer_may_multiplex(const struct Curl_easy *data, { #ifndef CURL_DISABLE_HTTP /* If an HTTP protocol and multiplexing is enabled */ - if((conn->handler->protocol & PROTO_FAMILY_HTTP) && + if((conn->scheme->protocol & PROTO_FAMILY_HTTP) && (!conn->bits.protoconnstart || !conn->bits.close)) { if(Curl_multiplex_wanted(data->multi) && @@ -590,37 +590,19 @@ static bool proxy_info_matches(const struct proxy_info *data, { if((data->proxytype == needle->proxytype) && (data->port == needle->port) && - curl_strequal(data->host.name, needle->host.name)) - return TRUE; + curl_strequal(data->host.name, needle->host.name)) { + if(Curl_timestrcmp(data->user, needle->user) || + Curl_timestrcmp(data->passwd, needle->passwd)) + return FALSE; + return TRUE; + } return FALSE; } - -static bool socks_proxy_info_matches(const struct proxy_info *data, - const struct proxy_info *needle) -{ - if(!proxy_info_matches(data, needle)) - return FALSE; - - /* the user information is case-sensitive - or at least it is not defined as case-insensitive - see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 */ - - /* curl_strequal does a case insensitive comparison, - so do not use it here! */ - if(Curl_timestrcmp(data->user, needle->user) || - Curl_timestrcmp(data->passwd, needle->passwd)) - return FALSE; - return TRUE; -} -#else -/* disabled, will not get called */ -#define proxy_info_matches(x, y) FALSE -#define socks_proxy_info_matches(x, y) FALSE #endif /* A connection has to have been idle for less than 'conn_max_idle_ms' - (the success rate is just too low after this), or created less than + (the success rate is too low after this), or created less than 'conn_max_age_ms' ago, to be subject for reuse. */ static bool conn_maxage(struct Curl_easy *data, struct connectdata *conn, @@ -668,7 +650,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn, /* avoid check if already too old */ dead = TRUE; } - else if(conn->handler->connection_check) { + else if(conn->scheme->run->connection_check) { /* The protocol has a special method for checking the state of the connection. Use it to check if the connection is dead. */ unsigned int state; @@ -677,7 +659,8 @@ bool Curl_conn_seems_dead(struct connectdata *conn, checking it */ Curl_attach_connection(data, conn); - state = conn->handler->connection_check(data, conn, CONNCHECK_ISDEAD); + state = conn->scheme->run->connection_check(data, conn, + CONNCHECK_ISDEAD); dead = (state & CONNRESULT_DEAD); /* detach the connection again */ Curl_detach_connection(data); @@ -722,10 +705,11 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data, /* briefly attach for action */ Curl_attach_connection(data, conn); - if(conn->handler->connection_check) { + if(conn->scheme->run->connection_check) { /* Do a protocol-specific keepalive check on the connection. */ unsigned int rc; - rc = conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); + rc = conn->scheme->run->connection_check(data, conn, + CONNCHECK_KEEPALIVE); if(rc & CONNRESULT_DEAD) result = CURLE_RECV_ERROR; } @@ -747,8 +731,8 @@ static bool ssh_config_matches(struct connectdata *one, sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN); sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN); - return (sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && - Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub)); + return sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && + Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub); } #endif @@ -759,6 +743,8 @@ struct url_conn_match { BIT(may_multiplex); BIT(want_ntlm_http); BIT(want_proxy_ntlm_http); + BIT(want_nego_http); + BIT(want_proxy_nego_http); BIT(wait_pipe); BIT(force_reuse); @@ -899,7 +885,7 @@ static bool url_match_multiplex_limits(struct connectdata *conn, static bool url_match_ssl_use(struct connectdata *conn, struct url_conn_match *m) { - if(m->needle->handler->flags & PROTOPT_SSL) { + if(m->needle->scheme->flags & PROTOPT_SSL) { /* We are looking for SSL, if `conn` does not do it, not a match. */ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) return FALSE; @@ -907,8 +893,8 @@ static bool url_match_ssl_use(struct connectdata *conn, else if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* If the protocol does not allow reuse of SSL connections OR is of another protocol family, not a match. */ - if(!(m->needle->handler->flags & PROTOPT_SSL_REUSE) || - (get_protocol_family(conn->handler) != m->needle->handler->protocol)) + if(!(m->needle->scheme->flags & PROTOPT_SSL_REUSE) || + (get_protocol_family(conn->scheme) != m->needle->scheme->protocol)) return FALSE; } return TRUE; @@ -923,7 +909,7 @@ static bool url_match_proxy_use(struct connectdata *conn, return FALSE; if(m->needle->bits.socksproxy && - !socks_proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) + !proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) return FALSE; if(m->needle->bits.httpproxy) { @@ -952,7 +938,7 @@ static bool url_match_proxy_use(struct connectdata *conn, return TRUE; } #else -#define url_match_proxy_use(c, m) ((void)c, (void)m, TRUE) +#define url_match_proxy_use(c, m) ((void)(c), (void)(m), TRUE) #endif #ifndef CURL_DISABLE_HTTP @@ -961,7 +947,7 @@ static bool url_match_http_multiplex(struct connectdata *conn, { if(m->may_multiplex && (m->data->state.http_neg.allowed & (CURL_HTTP_V2x | CURL_HTTP_V3x)) && - (m->needle->handler->protocol & CURLPROTO_HTTP) && + (m->needle->scheme->protocol & CURLPROTO_HTTP) && !conn->httpversion_seen) { if(m->data->set.pipewait) { infof(m->data, "Server upgrade does not support multiplex yet, wait"); @@ -980,26 +966,26 @@ static bool url_match_http_version(struct connectdata *conn, { /* If looking for HTTP and the HTTP versions allowed do not include * the HTTP version of conn, continue looking. */ - if((m->needle->handler->protocol & PROTO_FAMILY_HTTP)) { + if((m->needle->scheme->protocol & PROTO_FAMILY_HTTP)) { switch(Curl_conn_http_version(m->data, conn)) { case 30: if(!(m->data->state.http_neg.allowed & CURL_HTTP_V3x)) { DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T - ", we do not want h3", conn->connection_id)); + ", we do not want h3", conn->connection_id)); return FALSE; } break; case 20: if(!(m->data->state.http_neg.allowed & CURL_HTTP_V2x)) { DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T - ", we do not want h2", conn->connection_id)); + ", we do not want h2", conn->connection_id)); return FALSE; } break; default: if(!(m->data->state.http_neg.allowed & CURL_HTTP_V1x)) { DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T - ", we do not want h1", conn->connection_id)); + ", we do not want h1", conn->connection_id)); return FALSE; } break; @@ -1008,8 +994,8 @@ static bool url_match_http_version(struct connectdata *conn, return TRUE; } #else -#define url_match_http_multiplex(c, m) ((void)c, (void)m, TRUE) -#define url_match_http_version(c, m) ((void)c, (void)m, TRUE) +#define url_match_http_multiplex(c, m) ((void)(c), (void)(m), TRUE) +#define url_match_http_version(c, m) ((void)(c), (void)(m), TRUE) #endif static bool url_match_proto_config(struct connectdata *conn, @@ -1019,13 +1005,13 @@ static bool url_match_proto_config(struct connectdata *conn, return FALSE; #ifdef USE_SSH - if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_SSH) { + if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_SSH) { if(!ssh_config_matches(m->needle, conn)) return FALSE; } #endif #ifndef CURL_DISABLE_FTP - else if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_FTP) { + else if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_FTP) { if(!ftp_conns_match(m->needle, conn)) return FALSE; } @@ -1036,7 +1022,7 @@ static bool url_match_proto_config(struct connectdata *conn, static bool url_match_auth(struct connectdata *conn, struct url_conn_match *m) { - if(!(m->needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { + if(!(m->needle->scheme->flags & PROTOPT_CREDSPERREQUEST)) { /* This protocol requires credentials per connection, so verify that we are using the same name and password as well */ if(Curl_timestrcmp(m->needle->user, conn->user) || @@ -1062,23 +1048,23 @@ static bool url_match_destination(struct connectdata *conn, { /* Additional match requirements if talking TLS OR * not talking to an HTTP proxy OR using a tunnel through a proxy */ - if((m->needle->handler->flags & PROTOPT_SSL) + if((m->needle->scheme->flags & PROTOPT_SSL) #ifndef CURL_DISABLE_PROXY || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy #endif ) { - if(!curl_strequal(m->needle->handler->scheme, conn->handler->scheme)) { + if(!curl_strequal(m->needle->scheme->name, conn->scheme->name)) { /* `needle` and `conn` do not have the same scheme... */ - if(get_protocol_family(conn->handler) != m->needle->handler->protocol) { + if(get_protocol_family(conn->scheme) != m->needle->scheme->protocol) { /* and `conn`s protocol family is not the protocol `needle` wants. * IMAPS would work for IMAP, but no vice versa. */ return FALSE; } /* We are in an IMAPS vs IMAP like case. We expect `conn` to have SSL */ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - DEBUGF(infof(m->data, - "Connection #%" FMT_OFF_T " has compatible protocol family, " - "but no SSL, no match", conn->connection_id)); + DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T + " has compatible protocol family, but no SSL, no match", + conn->connection_id)); return FALSE; } } @@ -1102,10 +1088,9 @@ static bool url_match_ssl_config(struct connectdata *conn, struct url_conn_match *m) { /* If talking TLS, conn needs to use the same SSL options. */ - if((m->needle->handler->flags & PROTOPT_SSL) && + if((m->needle->scheme->flags & PROTOPT_SSL) && !Curl_ssl_conn_config_match(m->data, conn, FALSE)) { - DEBUGF(infof(m->data, - "Connection #%" FMT_OFF_T + DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T " has different SSL parameters, cannot reuse", conn->connection_id)); return FALSE; @@ -1178,7 +1163,64 @@ static bool url_match_auth_ntlm(struct connectdata *conn, return TRUE; } #else -#define url_match_auth_ntlm(c, m) ((void)c, (void)m, TRUE) +#define url_match_auth_ntlm(c, m) ((void)(c), (void)(m), TRUE) +#endif + +#ifdef USE_SPNEGO +static bool url_match_auth_nego(struct connectdata *conn, + struct url_conn_match *m) +{ + /* If we are looking for an HTTP+Negotiate connection, check if this is + already authenticating with the right credentials. If not, keep looking + so that we can reuse Negotiate connections if possible. */ + if(m->want_nego_http) { + if(Curl_timestrcmp(m->needle->user, conn->user) || + Curl_timestrcmp(m->needle->passwd, conn->passwd)) + return FALSE; + } + else if(conn->http_negotiate_state != GSS_AUTHNONE) { + /* Connection is using Negotiate auth but we do not want Negotiate */ + return FALSE; + } + +#ifndef CURL_DISABLE_PROXY + /* Same for Proxy Negotiate authentication */ + if(m->want_proxy_nego_http) { + /* Both conn->http_proxy.user and conn->http_proxy.passwd can be + * NULL */ + if(!conn->http_proxy.user || !conn->http_proxy.passwd) + return FALSE; + + if(Curl_timestrcmp(m->needle->http_proxy.user, + conn->http_proxy.user) || + Curl_timestrcmp(m->needle->http_proxy.passwd, + conn->http_proxy.passwd)) + return FALSE; + } + else if(conn->proxy_negotiate_state != GSS_AUTHNONE) { + /* Proxy connection is using Negotiate auth but we do not want Negotiate */ + return FALSE; + } +#endif + if(m->want_nego_http || m->want_proxy_nego_http) { + /* Credentials are already checked, we may use this connection. We MUST + * use a connection where it has already been fully negotiated. If it has + * not, we keep on looking for a better one. */ + m->found = conn; + if((m->want_nego_http && + (conn->http_negotiate_state != GSS_AUTHNONE)) || + (m->want_proxy_nego_http && + (conn->proxy_negotiate_state != GSS_AUTHNONE))) { + /* We must use this connection, no other */ + m->force_reuse = TRUE; + return TRUE; + } + return FALSE; /* get another */ + } + return TRUE; +} +#else +#define url_match_auth_nego(c, m) ((void)(c), (void)(m), TRUE) #endif static bool url_match_conn(struct connectdata *conn, void *userdata) @@ -1224,6 +1266,11 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) else if(m->force_reuse) return TRUE; + if(!url_match_auth_nego(conn, m)) + return FALSE; + else if(m->force_reuse) + return TRUE; + if(!url_match_multiplex_limits(conn, m)) return FALSE; @@ -1264,38 +1311,48 @@ static bool url_match_result(bool result, void *userdata) } /* - * Given one filled in connection struct (named needle), this function should - * detect if there already is one that has all the significant details - * exactly the same and thus should be used instead. + * Given a transfer and a prototype connection (needle), + * find and attach an existing connection that matches. * - * If there is a match, this function returns TRUE - and has marked the - * connection as 'in-use'. It must later be called with ConnectionDone() to - * return back to 'idle' (unused) state. - * - * The force_reuse flag is set if the connection must be used. + * Return TRUE if an existing connection was attached. + * `waitpipe` is TRUE if no existing connection matched, but there + * might be suitable one in the near future (common cause: multiplexing + * capability has not been determined yet, e.g. ALPN handshake). */ -static bool ConnectionExists(struct Curl_easy *data, - struct connectdata *needle, - struct connectdata **usethis, - bool *force_reuse, - bool *waitpipe) +static bool url_attach_existing(struct Curl_easy *data, + struct connectdata *needle, + bool *waitpipe) { struct url_conn_match match; bool result; + DEBUGASSERT(!data->conn); memset(&match, 0, sizeof(match)); match.data = data; match.needle = needle; match.may_multiplex = xfer_may_multiplex(data, needle); #ifdef USE_NTLM - match.want_ntlm_http = ((data->state.authhost.want & CURLAUTH_NTLM) && - (needle->handler->protocol & PROTO_FAMILY_HTTP)); + match.want_ntlm_http = + (data->state.authhost.want & CURLAUTH_NTLM) && + (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_ntlm_http = - (needle->bits.proxy_user_passwd && - (data->state.authproxy.want & CURLAUTH_NTLM) && - (needle->handler->protocol & PROTO_FAMILY_HTTP)); + needle->bits.proxy_user_passwd && + (data->state.authproxy.want & CURLAUTH_NTLM) && + (needle->scheme->protocol & PROTO_FAMILY_HTTP); +#endif +#endif + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) + match.want_nego_http = + (data->state.authhost.want & CURLAUTH_NEGOTIATE) && + (needle->scheme->protocol & PROTO_FAMILY_HTTP); +#ifndef CURL_DISABLE_PROXY + match.want_proxy_nego_http = + needle->bits.proxy_user_passwd && + (data->state.authproxy.want & CURLAUTH_NEGOTIATE) && + (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif #endif @@ -1306,9 +1363,7 @@ static bool ConnectionExists(struct Curl_easy *data, /* wait_pipe is TRUE if we encounter a bundle that is undecided. There * is no matching connection then, yet. */ - *usethis = match.found; - *force_reuse = match.force_reuse; - *waitpipe = match.wait_pipe; + *waitpipe = (bool)match.wait_pipe; return result; } @@ -1341,7 +1396,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->http_proxy.proxytype = data->set.proxytype; conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; - /* note that these two proxy bits are now just on what looks to be + /* note that these two proxy bits are set on what looks to be requested, they may be altered down the road */ conn->bits.proxy = (data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]); @@ -1365,7 +1420,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; #endif conn->ip_version = data->set.ipver; - conn->connect_only = data->set.connect_only; + conn->connect_only = (bool)data->set.connect_only; conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ /* Store the local bind parameters that will be used for this connection */ @@ -1396,14 +1451,15 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) return NULL; } -const struct Curl_handler *Curl_get_scheme_handler(const char *scheme) +const struct Curl_scheme *Curl_get_scheme(const char *scheme) { - return Curl_getn_scheme_handler(scheme, strlen(scheme)); + return Curl_getn_scheme(scheme, strlen(scheme)); } -/* returns the handler if the given scheme is built-in */ -const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, - size_t len) +/* Returns a struct scheme pointer if the name is a known scheme. Check the + ->run struct field for non-NULL to figure out if an implementation is + present. */ +const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) { /* table generated by schemetable.c: 1. gcc schemetable.c && ./a.out @@ -1413,194 +1469,47 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, 5. copy the table into this source code 6. make sure this function uses the same hash function that worked for schemetable.c - 7. if needed, adjust the #ifdefs in schemetable.c and rerun */ - static const struct Curl_handler * const protocols[67] = { -#ifndef CURL_DISABLE_FILE - &Curl_handler_file, -#else - NULL, -#endif - NULL, NULL, -#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER) - &Curl_handler_gophers, -#else - NULL, -#endif - NULL, -#ifdef USE_LIBRTMP - &Curl_handler_rtmpe, -#else - NULL, -#endif -#ifndef CURL_DISABLE_SMTP - &Curl_handler_smtp, -#else - NULL, -#endif -#ifdef USE_SSH - &Curl_handler_sftp, -#else - NULL, -#endif -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - &Curl_handler_smb, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP) - &Curl_handler_smtps, -#else - NULL, -#endif -#ifndef CURL_DISABLE_TELNET - &Curl_handler_telnet, -#else - NULL, -#endif -#ifndef CURL_DISABLE_GOPHER - &Curl_handler_gopher, -#else - NULL, -#endif -#ifndef CURL_DISABLE_TFTP - &Curl_handler_tftp, -#else - NULL, -#endif - NULL, NULL, NULL, -#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) - &Curl_handler_ftps, -#else - NULL, -#endif -#ifndef CURL_DISABLE_HTTP - &Curl_handler_http, -#else - NULL, -#endif -#ifndef CURL_DISABLE_IMAP - &Curl_handler_imap, -#else - NULL, -#endif -#ifdef USE_LIBRTMP - &Curl_handler_rtmps, -#else - NULL, -#endif -#ifdef USE_LIBRTMP - &Curl_handler_rtmpt, -#else - NULL, -#endif - NULL, NULL, NULL, -#if !defined(CURL_DISABLE_LDAP) && \ - !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) - &Curl_handler_ldaps, -#else - NULL, -#endif -#if !defined(CURL_DISABLE_WEBSOCKETS) && \ - defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_wss, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_https, -#else - NULL, -#endif - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -#ifndef CURL_DISABLE_RTSP - &Curl_handler_rtsp, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_SMB) && \ - defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4) - &Curl_handler_smbs, -#else - NULL, -#endif -#ifdef USE_SSH - &Curl_handler_scp, -#else - NULL, -#endif - NULL, NULL, NULL, -#ifndef CURL_DISABLE_POP3 - &Curl_handler_pop3, -#else - NULL, -#endif - NULL, NULL, -#ifdef USE_LIBRTMP - &Curl_handler_rtmp, -#else - NULL, -#endif - NULL, NULL, NULL, -#ifdef USE_LIBRTMP - &Curl_handler_rtmpte, -#else - NULL, -#endif - NULL, NULL, NULL, -#ifndef CURL_DISABLE_DICT - &Curl_handler_dict, -#else - NULL, -#endif - NULL, NULL, NULL, -#ifndef CURL_DISABLE_MQTT - &Curl_handler_mqtt, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) - &Curl_handler_pop3s, -#else - NULL, -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP) - &Curl_handler_imaps, -#else - NULL, -#endif - NULL, -#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_ws, -#else - NULL, -#endif - NULL, -#ifdef USE_LIBRTMP - &Curl_handler_rtmpts, -#else - NULL, -#endif -#ifndef CURL_DISABLE_LDAP - &Curl_handler_ldap, -#else - NULL, -#endif - NULL, NULL, -#ifndef CURL_DISABLE_FTP - &Curl_handler_ftp, -#else - NULL, -#endif + static const struct Curl_scheme * const all_schemes[67] = { + &Curl_scheme_file, + &Curl_scheme_mqtts, NULL, + &Curl_scheme_gophers, NULL, + &Curl_scheme_rtmpe, + &Curl_scheme_smtp, + &Curl_scheme_sftp, + &Curl_scheme_smb, + &Curl_scheme_smtps, + &Curl_scheme_telnet, + &Curl_scheme_gopher, + &Curl_scheme_tftp, NULL, NULL, NULL, + &Curl_scheme_ftps, + &Curl_scheme_http, + &Curl_scheme_imap, + &Curl_scheme_rtmps, + &Curl_scheme_rtmpt, NULL, NULL, NULL, + &Curl_scheme_ldaps, + &Curl_scheme_wss, + &Curl_scheme_https, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &Curl_scheme_rtsp, + &Curl_scheme_smbs, + &Curl_scheme_scp, NULL, NULL, NULL, + &Curl_scheme_pop3, NULL, NULL, + &Curl_scheme_rtmp, NULL, NULL, NULL, + &Curl_scheme_rtmpte, NULL, NULL, NULL, + &Curl_scheme_dict, NULL, NULL, NULL, + &Curl_scheme_mqtt, + &Curl_scheme_pop3s, + &Curl_scheme_imaps, NULL, + &Curl_scheme_ws, NULL, + &Curl_scheme_rtmpts, + &Curl_scheme_ldap, NULL, NULL, + &Curl_scheme_ftp, }; if(len && (len <= 7)) { const char *s = scheme; size_t l = len; - const struct Curl_handler *h; + const struct Curl_scheme *h; unsigned int c = 978; while(l) { c <<= 5; @@ -1609,8 +1518,8 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, l--; } - h = protocols[c % 67]; - if(h && curl_strnequal(scheme, h->scheme, len) && !h->scheme[len]) + h = all_schemes[c % 67]; + if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) return h; } return NULL; @@ -1620,9 +1529,9 @@ static CURLcode findprotocol(struct Curl_easy *data, struct connectdata *conn, const char *protostr) { - const struct Curl_handler *p = Curl_get_scheme_handler(protostr); + const struct Curl_scheme *p = Curl_get_scheme(protostr); - if(p && /* Protocol found in table. Check if allowed */ + if(p && p->run && /* Protocol found supported. Check if allowed */ (data->set.allowed_protocols & p->protocol)) { /* it is allowed for "normal" request, now do an extra check if this is @@ -1633,7 +1542,7 @@ static CURLcode findprotocol(struct Curl_easy *data, ; else { /* Perform setup complement if some. */ - conn->handler = conn->given = p; + conn->scheme = conn->given = p; /* 'port' and 'remote_port' are set in setup_connection_internals() */ return CURLE_OK; } @@ -1674,7 +1583,7 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, { char *zoneid; CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); -#ifdef CURL_DISABLE_VERBOSE_STRINGS +#if !defined(HAVE_IF_NAMETOINDEX) || !defined(CURLVERBOSE) (void)data; #endif @@ -1686,20 +1595,11 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, conn->scope_id = (unsigned int)scope; #ifdef HAVE_IF_NAMETOINDEX else { -#elif defined(_WIN32) - else if(Curl_if_nametoindex) { -#endif - -#if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32) /* Zone identifier is not numeric */ unsigned int scopeidx = 0; -#ifdef HAVE_IF_NAMETOINDEX scopeidx = if_nametoindex(zoneid); -#else - scopeidx = Curl_if_nametoindex(zoneid); -#endif if(!scopeidx) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE char buffer[STRERROR_LEN]; infof(data, "Invalid zoneid: %s; %s", zoneid, curlx_strerror(errno, buffer, sizeof(buffer))); @@ -1708,7 +1608,7 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, else conn->scope_id = scopeidx; } -#endif /* HAVE_IF_NAMETOINDEX || _WIN32 */ +#endif /* HAVE_IF_NAMETOINDEX */ curlx_free(zoneid); } @@ -1852,7 +1752,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, if(!uc) { char *decoded; result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, - conn->handler->flags&PROTOPT_USERPWDCTRL ? + conn->scheme->flags&PROTOPT_USERPWDCTRL ? REJECT_ZERO : REJECT_CTRL); if(result) return result; @@ -1874,7 +1774,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, if(!uc) { char *decoded; result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, - conn->handler->flags&PROTOPT_USERPWDCTRL ? + conn->scheme->flags&PROTOPT_USERPWDCTRL ? REJECT_ZERO : REJECT_CTRL); if(result) return result; @@ -1929,7 +1829,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, #ifdef USE_IPV6 if(data->set.scope_id) - /* Override any scope that was set above. */ + /* Override any scope that was set above. */ conn->scope_id = data->set.scope_id; #endif @@ -1984,8 +1884,8 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, CURLcode result; DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); - if(conn->handler->setup_connection) { - result = conn->handler->setup_connection(data, conn); + if(conn->scheme->run->setup_connection) { + result = conn->scheme->run->setup_connection(data, conn); if(result) return result; } @@ -2053,13 +1953,11 @@ static char *detect_proxy(struct Curl_easy *data, * checked if the lowercase versions do not exist. */ char proxy_env[20]; - const char *envp = proxy_env; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif + const char *envp; + VERBOSE(envp = proxy_env); curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", - conn->handler->scheme); + conn->scheme->name); /* read the protocol proxy: */ proxy = curl_getenv(proxy_env); @@ -2118,7 +2016,7 @@ static char *detect_proxy(struct Curl_easy *data, * that may exist registered to the same proxy host. */ static CURLcode parse_proxy(struct Curl_easy *data, - struct connectdata *conn, char *proxy, + struct connectdata *conn, const char *proxy, long proxytype) { char *portptr = NULL; @@ -2420,13 +2318,13 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, } #endif - if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { + if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { curlx_free(proxy); /* Do not bother with an empty proxy string or if the protocol does not work with network */ proxy = NULL; } if(socksproxy && (!*socksproxy || - (conn->handler->flags & PROTOPT_NONETWORK))) { + (conn->scheme->flags & PROTOPT_NONETWORK))) { curlx_free(socksproxy); /* Do not bother with an empty socks proxy string or if the protocol does not work with network */ @@ -2462,10 +2360,10 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, goto out; #else /* force this connection's protocol to become HTTP if compatible */ - if(!(conn->handler->protocol & PROTO_FAMILY_HTTP)) { - if((conn->handler->flags & PROTOPT_PROXY_AS_HTTP) && + if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { + if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) && !conn->bits.tunnel_proxy) - conn->handler = &Curl_handler_http; + conn->scheme = &Curl_scheme_http; else /* if not converting to HTTP over the proxy, enforce tunneling */ conn->bits.tunnel_proxy = TRUE; @@ -2584,13 +2482,13 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, (size_t)(login + len - osep)) - 1 : 0); /* Clone the user portion buffer, which can be zero length */ - ubuf = Curl_memdup0(login, ulen); + ubuf = curlx_memdup0(login, ulen); if(!ubuf) goto error; /* Clone the password portion buffer */ if(psep) { - pbuf = Curl_memdup0(&psep[1], plen); + pbuf = curlx_memdup0(&psep[1], plen); if(!pbuf) goto error; } @@ -2599,7 +2497,7 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, if(optionsp) { char *obuf = NULL; if(olen) { - obuf = Curl_memdup0(&osep[1], olen); + obuf = curlx_memdup0(&osep[1], olen); if(!obuf) goto error; } @@ -2625,7 +2523,6 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, static CURLcode parse_remote_port(struct Curl_easy *data, struct connectdata *conn) { - if(data->set.use_port && data->state.allow_port) { /* if set, we use this instead of the port possibly given in the URL */ char portbuf[16]; @@ -2710,7 +2607,7 @@ static CURLcode override_login(struct Curl_easy *data, return CURLE_READ_ERROR; } else { - if(!(conn->handler->flags & PROTOPT_USERPWDCTRL)) { + if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { /* if the protocol cannot handle control codes in credentials, make sure there are none */ if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) { @@ -2793,7 +2690,7 @@ static CURLcode set_login(struct Curl_easy *data, const char *setpasswd = CURL_DEFAULT_PASSWORD; /* If our protocol needs a password and we have none, use the defaults */ - if((conn->handler->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) + if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) ; else { setuser = ""; @@ -2833,10 +2730,6 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, int port = -1; CURLcode result = CURLE_OK; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - *hostname_result = NULL; *port_result = -1; @@ -2968,7 +2861,7 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, } else { /* check whether the URL's port matches */ - char *ptr_next = strchr(ptr, ':'); + const char *ptr_next = strchr(ptr, ':'); if(ptr_next) { curl_off_t port_to_match; if(!curlx_str_number(&ptr, &port_to_match, 0xffff) && @@ -3034,7 +2927,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, #ifndef CURL_DISABLE_ALTSVC if(data->asi && !host && (port == -1) && - ((conn->handler->protocol == CURLPROTO_HTTPS) || + ((conn->scheme->protocol == CURLPROTO_HTTPS) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") @@ -3114,7 +3007,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, } } else if(hit) { - char *hostd = curlx_strdup((char *)as->dst.host); + char *hostd = curlx_strdup(as->dst.host); if(!hostd) return CURLE_OUT_OF_MEMORY; conn->conn_to_host.rawalloc = hostd; @@ -3154,7 +3047,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, #ifdef USE_UNIX_SOCKETS static CURLcode resolve_unix(struct Curl_easy *data, struct connectdata *conn, - char *unix_path, + const char *unix_path, struct Curl_dns_entry **pdns) { struct Curl_dns_entry *hostaddr; @@ -3163,15 +3056,15 @@ static CURLcode resolve_unix(struct Curl_easy *data, DEBUGASSERT(unix_path); *pdns = NULL; - /* Unix domain sockets are local. The host gets ignored, just use the - * specified domain socket address. Do not cache "DNS entries". There is - * no DNS involved and we already have the file system path available. */ + /* Unix domain sockets are local. The host gets ignored, use the specified + * domain socket address. Do not cache "DNS entries". There is no DNS + * involved and we already have the file system path available. */ hostaddr = curlx_calloc(1, sizeof(struct Curl_dns_entry)); if(!hostaddr) return CURLE_OUT_OF_MEMORY; hostaddr->addr = Curl_unix2addr(unix_path, &longpath, - conn->bits.abstract_unix_socket); + (bool)conn->bits.abstract_unix_socket); if(!hostaddr->addr) { if(longpath) /* Long paths are not supported for now */ @@ -3196,7 +3089,7 @@ static CURLcode resolve_server(struct Curl_easy *data, { struct hostname *ehost; int eport; - timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE); + timediff_t timeout_ms = Curl_timeleft_ms(data); const char *peertype = "host"; CURLcode result; @@ -3204,7 +3097,7 @@ static CURLcode resolve_server(struct Curl_easy *data, #ifdef USE_UNIX_SOCKETS { - char *unix_path = conn->unix_domain_socket; + const char *unix_path = conn->unix_domain_socket; #ifndef CURL_DISABLE_PROXY if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name && @@ -3238,12 +3131,7 @@ static CURLcode resolve_server(struct Curl_easy *data, eport = conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port; } - /* Resolve target host right on */ - conn->hostname_resolve = curlx_strdup(ehost->name); - if(!conn->hostname_resolve) - return CURLE_OUT_OF_MEMORY; - - result = Curl_resolv_timeout(data, conn->hostname_resolve, + result = Curl_resolv_timeout(data, ehost->name, eport, conn->ip_version, pdns, timeout_ms); DEBUGASSERT(!result || !*pdns); @@ -3275,42 +3163,41 @@ static void url_move_hostname(struct hostname *dest, struct hostname *src) } /* - * Cleanup the connection `temp`, just allocated for `data`, before using the - * previously `existing` one for `data`. All relevant info is copied over - * and `temp` is freed. + * Adjust reused connection settings to the transfer/needle. */ -static void reuse_conn(struct Curl_easy *data, - struct connectdata *temp, - struct connectdata *existing) +static void url_conn_reuse_adjust(struct Curl_easy *data, + struct connectdata *needle) { - /* get the user+password information from the temp struct since it may - * be new for this request even when we reuse an existing connection */ - if(temp->user) { + struct connectdata *conn = data->conn; + + /* get the user+password information from the needle since it may + * be new for this request even when we reuse conn */ + if(needle->user) { /* use the new username and password though */ - curlx_free(existing->user); - curlx_free(existing->passwd); - existing->user = temp->user; - existing->passwd = temp->passwd; - temp->user = NULL; - temp->passwd = NULL; + curlx_free(conn->user); + curlx_free(conn->passwd); + conn->user = needle->user; + conn->passwd = needle->passwd; + needle->user = NULL; + needle->passwd = NULL; } #ifndef CURL_DISABLE_PROXY - existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd; - if(existing->bits.proxy_user_passwd) { + conn->bits.proxy_user_passwd = needle->bits.proxy_user_passwd; + if(conn->bits.proxy_user_passwd) { /* use the new proxy username and proxy password though */ - curlx_free(existing->http_proxy.user); - curlx_free(existing->socks_proxy.user); - curlx_free(existing->http_proxy.passwd); - curlx_free(existing->socks_proxy.passwd); - existing->http_proxy.user = temp->http_proxy.user; - existing->socks_proxy.user = temp->socks_proxy.user; - existing->http_proxy.passwd = temp->http_proxy.passwd; - existing->socks_proxy.passwd = temp->socks_proxy.passwd; - temp->http_proxy.user = NULL; - temp->socks_proxy.user = NULL; - temp->http_proxy.passwd = NULL; - temp->socks_proxy.passwd = NULL; + curlx_free(conn->http_proxy.user); + curlx_free(conn->socks_proxy.user); + curlx_free(conn->http_proxy.passwd); + curlx_free(conn->socks_proxy.passwd); + conn->http_proxy.user = needle->http_proxy.user; + conn->socks_proxy.user = needle->socks_proxy.user; + conn->http_proxy.passwd = needle->http_proxy.passwd; + conn->socks_proxy.passwd = needle->socks_proxy.passwd; + needle->http_proxy.user = NULL; + needle->socks_proxy.user = NULL; + needle->http_proxy.passwd = NULL; + needle->socks_proxy.passwd = NULL; } #endif @@ -3323,24 +3210,13 @@ static void reuse_conn(struct Curl_easy *data, * a new request to the same remote endpoint of an existing conn. * We want to reuse an existing conn to the remote endpoint. * Since connection reuse does not match on conn->host necessarily, we - * switch `existing` conn to `temp` conn's host settings. - * Is this correct in the case of TLS connections that have - * used the original hostname in SNI to negotiate? Do we send - * requests for another host through the different SNI? + * switch conn to needle's host settings. */ - url_move_hostname(&existing->host, &temp->host); - url_move_hostname(&existing->conn_to_host, &temp->conn_to_host); - - existing->conn_to_port = temp->conn_to_port; - existing->remote_port = temp->remote_port; - curlx_free(existing->hostname_resolve); - existing->hostname_resolve = temp->hostname_resolve; - temp->hostname_resolve = NULL; - - /* reuse init */ - existing->bits.reuse = TRUE; /* yes, we are reusing here */ + url_move_hostname(&conn->host, &needle->host); + url_move_hostname(&conn->conn_to_host, &needle->conn_to_host); - Curl_conn_free(data, temp); + conn->conn_to_port = needle->conn_to_port; + conn->remote_port = needle->remote_port; } static void conn_meta_freeentry(void *p) @@ -3352,35 +3228,11 @@ static void conn_meta_freeentry(void *p) DEBUGASSERT(p == NULL); } -/** - * create_conn() sets up a new connectdata struct, or reuses an already - * existing one, and resolves hostname. - * - * if this function returns CURLE_OK and *async is set to TRUE, the resolve - * response will be coming asynchronously. If *async is FALSE, the name is - * already resolved. - * - * @param data The sessionhandle pointer - * @param in_connect is set to the next connection data pointer - * @param reusedp is set to to TRUE if connection was reused - * @see Curl_setup_conn() - * - */ - -static CURLcode create_conn(struct Curl_easy *data, - struct connectdata **in_connect, - bool *reusedp) +static CURLcode url_create_needle(struct Curl_easy *data, + struct connectdata **pneedle) { + struct connectdata *needle = NULL; CURLcode result = CURLE_OK; - struct connectdata *conn; - struct connectdata *existing = NULL; - bool reuse; - bool connections_available = TRUE; - bool force_reuse = FALSE; - bool waitpipe = FALSE; - - *reusedp = FALSE; - *in_connect = NULL; /************************************************************* * Check input data @@ -3394,37 +3246,31 @@ static CURLcode create_conn(struct Curl_easy *data, parts for checking against the already present connections. In order to not have to modify everything at once, we allocate a temporary connection data struct and fill in for comparison purposes. */ - conn = allocate_conn(data); - - if(!conn) { + needle = allocate_conn(data); + if(!needle) { result = CURLE_OUT_OF_MEMORY; goto out; } - /* We must set the return variable as soon as possible, so that our - parent can cleanup any possible allocs we may have done before - any failure */ - *in_connect = conn; - /* Do the unfailable inits first, before checks that may early return */ - Curl_hash_init(&conn->meta_hash, 23, + Curl_hash_init(&needle->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry); - result = parseurlandfillconn(data, conn); + result = parseurlandfillconn(data, needle); if(result) goto out; if(data->set.str[STRING_SASL_AUTHZID]) { - conn->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); - if(!conn->sasl_authzid) { + needle->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); + if(!needle->sasl_authzid) { result = CURLE_OUT_OF_MEMORY; goto out; } } if(data->set.str[STRING_BEARER]) { - conn->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]); - if(!conn->oauth_bearer) { + needle->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]); + if(!needle->oauth_bearer) { result = CURLE_OUT_OF_MEMORY; goto out; } @@ -3432,20 +3278,20 @@ static CURLcode create_conn(struct Curl_easy *data, #ifdef USE_UNIX_SOCKETS if(data->set.str[STRING_UNIX_SOCKET_PATH]) { - conn->unix_domain_socket = + needle->unix_domain_socket = curlx_strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); - if(!conn->unix_domain_socket) { + if(!needle->unix_domain_socket) { result = CURLE_OUT_OF_MEMORY; goto out; } - conn->bits.abstract_unix_socket = data->set.abstract_unix_socket; + needle->bits.abstract_unix_socket = data->set.abstract_unix_socket; } #endif /* After the Unix socket init but before the proxy vars are used, parse and initialize the proxy vars */ #ifndef CURL_DISABLE_PROXY - result = create_conn_helper_init_proxy(data, conn); + result = create_conn_helper_init_proxy(data, needle); if(result) goto out; @@ -3453,24 +3299,24 @@ static CURLcode create_conn(struct Curl_easy *data, * If the protocol is using SSL and HTTP proxy is used, we set * the tunnel_proxy bit. *************************************************************/ - if((conn->given->flags & PROTOPT_SSL) && conn->bits.httpproxy) - conn->bits.tunnel_proxy = TRUE; + if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy) + needle->bits.tunnel_proxy = TRUE; #endif /************************************************************* * Figure out the remote port number and fix it in the URL *************************************************************/ - result = parse_remote_port(data, conn); + result = parse_remote_port(data, needle); if(result) goto out; /* Check for overridden login details and set them accordingly so that they are known when protocol->setup_connection is called! */ - result = override_login(data, conn); + result = override_login(data, needle); if(result) goto out; - result = set_login(data, conn); /* default credentials */ + result = set_login(data, needle); /* default credentials */ if(result) goto out; @@ -3478,7 +3324,7 @@ static CURLcode create_conn(struct Curl_easy *data, * Process the "connect to" linked list of hostname/port mappings. * Do this after the remote port number has been fixed in the URL. *************************************************************/ - result = parse_connect_to_slist(data, conn, data->set.connect_to); + result = parse_connect_to_slist(data, needle, data->set.connect_to); if(result) goto out; @@ -3486,38 +3332,39 @@ static CURLcode create_conn(struct Curl_easy *data, * IDN-convert the proxy hostnames *************************************************************/ #ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy) { - result = Curl_idnconvert_hostname(&conn->http_proxy.host); + if(needle->bits.httpproxy) { + result = Curl_idnconvert_hostname(&needle->http_proxy.host); if(result) - return result; + goto out; } - if(conn->bits.socksproxy) { - result = Curl_idnconvert_hostname(&conn->socks_proxy.host); + if(needle->bits.socksproxy) { + result = Curl_idnconvert_hostname(&needle->socks_proxy.host); if(result) - return result; + goto out; } #endif - if(conn->bits.conn_to_host) { - result = Curl_idnconvert_hostname(&conn->conn_to_host); + if(needle->bits.conn_to_host) { + result = Curl_idnconvert_hostname(&needle->conn_to_host); if(result) - return result; + goto out; } /************************************************************* * Check whether the host and the "connect to host" are equal. * Do this after the hostnames have been IDN-converted. *************************************************************/ - if(conn->bits.conn_to_host && - curl_strequal(conn->conn_to_host.name, conn->host.name)) { - conn->bits.conn_to_host = FALSE; + if(needle->bits.conn_to_host && + curl_strequal(needle->conn_to_host.name, needle->host.name)) { + needle->bits.conn_to_host = FALSE; } /************************************************************* * Check whether the port and the "connect to port" are equal. * Do this after the remote port number has been fixed in the URL. *************************************************************/ - if(conn->bits.conn_to_port && conn->conn_to_port == conn->remote_port) { - conn->bits.conn_to_port = FALSE; + if(needle->bits.conn_to_port && + needle->conn_to_port == needle->remote_port) { + needle->bits.conn_to_port = FALSE; } #ifndef CURL_DISABLE_PROXY @@ -3525,105 +3372,141 @@ static CURLcode create_conn(struct Curl_easy *data, * If the "connect to" feature is used with an HTTP proxy, * we set the tunnel_proxy bit. *************************************************************/ - if((conn->bits.conn_to_host || conn->bits.conn_to_port) && - conn->bits.httpproxy) - conn->bits.tunnel_proxy = TRUE; + if((needle->bits.conn_to_host || needle->bits.conn_to_port) && + needle->bits.httpproxy) + needle->bits.tunnel_proxy = TRUE; #endif /************************************************************* * Setup internals depending on protocol. Needs to be done after * we figured out what/if proxy to use. *************************************************************/ - result = setup_connection_internals(data, conn); + result = setup_connection_internals(data, needle); if(result) goto out; + if(needle->scheme->flags & PROTOPT_ALPN) { + /* The protocol wants it, so set the bits if enabled in the easy handle + (default) */ + if(data->set.ssl_enable_alpn) + needle->bits.tls_enable_alpn = TRUE; + } + + if(!(needle->scheme->flags & PROTOPT_NONETWORK)) { + /* Setup callbacks for network connections */ + needle->recv[FIRSTSOCKET] = Curl_cf_recv; + needle->send[FIRSTSOCKET] = Curl_cf_send; + needle->recv[SECONDARYSOCKET] = Curl_cf_recv; + needle->send[SECONDARYSOCKET] = Curl_cf_send; + needle->bits.tcp_fastopen = data->set.tcp_fastopen; + } + +out: + if(!result) { + DEBUGASSERT(needle); + *pneedle = needle; + } + else { + *pneedle = NULL; + if(needle) + Curl_conn_free(data, needle); + } + return result; +} + +/** + * Find an existing connection for the transfer or create a new one. + * Returns + * - CURLE_OK on success with a connection attached to data + * - CURLE_NO_CONNECTION_AVAILABLE when connection limits apply or when + * a suitable connection has not determined its multiplex capability. + * - a fatal error + */ +static CURLcode url_find_or_create_conn(struct Curl_easy *data) +{ + struct connectdata *needle = NULL; + bool waitpipe = FALSE; + CURLcode result; + + /* create the template connection for transfer data. Use this needle to + * find an existing connection or, if none exists, convert needle + * to a full connection and attach it to data. */ + result = url_create_needle(data, &needle); + if(result) + goto out; + DEBUGASSERT(needle); + /*********************************************************************** * file: is a special case in that it does not need a network connection ***********************************************************************/ #ifndef CURL_DISABLE_FILE - if(conn->handler->flags & PROTOPT_NONETWORK) { + if(needle->scheme->flags & PROTOPT_NONETWORK) { bool done; /* this is supposed to be the connect function so we better at least check that the file is present here! */ - DEBUGASSERT(conn->handler->connect_it); - data->info.conn_scheme = conn->handler->scheme; + DEBUGASSERT(needle->scheme->run->connect_it); + data->info.conn_scheme = needle->scheme->name; /* conn_protocol can only provide "old" protocols */ - data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; - result = conn->handler->connect_it(data, &done); + data->info.conn_protocol = (needle->scheme->protocol) & CURLPROTO_MASK; + result = needle->scheme->run->connect_it(data, &done); if(result) goto out; /* Setup a "faked" transfer that will do nothing */ - Curl_attach_connection(data, conn); - result = Curl_cpool_add(data, conn); + Curl_attach_connection(data, needle); + needle = NULL; + result = Curl_cpool_add(data, data->conn); if(!result) { /* Setup whatever necessary for a resumed transfer */ result = setup_range(data); if(!result) { Curl_xfer_setup_nop(data); - result = Curl_init_do(data, conn); + result = Curl_init_do(data, data->conn); } } if(result) { - DEBUGASSERT(conn->handler->done); + DEBUGASSERT(data->conn->scheme->run->done); /* we ignore the return code for the protocol-specific DONE */ - (void)conn->handler->done(data, result, FALSE); + (void)data->conn->scheme->run->done(data, result, FALSE); } goto out; } #endif - /* Setup filter for network connections */ - conn->recv[FIRSTSOCKET] = Curl_cf_recv; - conn->send[FIRSTSOCKET] = Curl_cf_send; - conn->recv[SECONDARYSOCKET] = Curl_cf_recv; - conn->send[SECONDARYSOCKET] = Curl_cf_send; - conn->bits.tcp_fastopen = data->set.tcp_fastopen; - /* Complete the easy's SSL configuration for connection cache matching */ result = Curl_ssl_easy_config_complete(data); if(result) goto out; + /* Get rid of any dead connections so limit are easier kept. */ Curl_cpool_prune_dead(data); /************************************************************* - * Check the current list of connections to see if we can - * reuse an already existing one or if we have to create a - * new one. + * Reuse of existing connection is not allowed when + * - connect_only is set or + * - reuse_fresh is set and this is not a follow-up request + * (like with HTTP followlocation) *************************************************************/ + if((!data->set.reuse_fresh || data->state.followlocation) && + !data->set.connect_only) { + /* Ok, try to find and attach an existing one */ + url_attach_existing(data, needle, &waitpipe); + } - DEBUGASSERT(conn->user); - DEBUGASSERT(conn->passwd); - - /* reuse_fresh is TRUE if we are told to use a new connection by force, but - we only acknowledge this option if this is not a reused connection - already (which happens due to follow-location or during an HTTP - authentication phase). CONNECT_ONLY transfers also refuse reuse. */ - if((data->set.reuse_fresh && !data->state.followlocation) || - data->set.connect_only) - reuse = FALSE; - else - reuse = ConnectionExists(data, conn, &existing, &force_reuse, &waitpipe); - - if(reuse) { - /* - * We already have a connection for this, we got the former connection in - * `existing` and thus we need to cleanup the one we just - * allocated before we can move along and use `existing`. - */ - bool tls_upgraded = (!(conn->given->flags & PROTOPT_SSL) && - Curl_conn_is_ssl(conn, FIRSTSOCKET)); + if(data->conn) { + /* We attached an existing connection for this transfer. Copy + * over transfer specific properties over from needle. */ + struct connectdata *conn = data->conn; + VERBOSE(bool tls_upgraded = (!(needle->given->flags & PROTOPT_SSL) && + Curl_conn_is_ssl(conn, FIRSTSOCKET))); - reuse_conn(data, conn, existing); - conn = existing; - *in_connect = conn; + conn->bits.reuse = TRUE; + url_conn_reuse_adjust(data, needle); #ifndef CURL_DISABLE_PROXY infof(data, "Reusing existing %s: connection%s with %s %s", - conn->given->scheme, + conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", conn->bits.proxy ? "proxy" : "host", conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : @@ -3631,36 +3514,30 @@ static CURLcode create_conn(struct Curl_easy *data, conn->host.dispname); #else infof(data, "Reusing existing %s: connection%s with host %s", - conn->given->scheme, + conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", conn->host.dispname); #endif } else { - /* We have decided that we want a new connection. However, we may not - be able to do that if we have reached the limit of how many - connections we are allowed to open. */ - DEBUGF(infof(data, "new connection, bits.close=%d", conn->bits.close)); - - if(conn->handler->flags & PROTOPT_ALPN) { - /* The protocol wants it, so set the bits if enabled in the easy handle - (default) */ - if(data->set.ssl_enable_alpn) - conn->bits.tls_enable_alpn = TRUE; - } + /* We have decided that we want a new connection. We may not be able to do + that if we have reached the limit of how many connections we are + allowed to open. */ + DEBUGF(infof(data, "new connection, bits.close=%d", needle->bits.close)); if(waitpipe) { /* There is a connection that *might* become usable for multiplexing "soon", and we wait for that */ infof(data, "Waiting on connection to negotiate possible multiplexing."); - connections_available = FALSE; + result = CURLE_NO_CONNECTION_AVAILABLE; + goto out; } else { - switch(Curl_cpool_check_limits(data, conn)) { + switch(Curl_cpool_check_limits(data, needle)) { case CPOOL_LIMIT_DEST: infof(data, "No more connections allowed to host"); - connections_available = FALSE; - break; + result = CURLE_NO_CONNECTION_AVAILABLE; + goto out; case CPOOL_LIMIT_TOTAL: if(data->master_mid != UINT32_MAX) CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override " @@ -3668,7 +3545,8 @@ static CURLcode create_conn(struct Curl_easy *data, else { infof(data, "No connections available, total of %zu reached.", data->multi->max_total_connections); - connections_available = FALSE; + result = CURLE_NO_CONNECTION_AVAILABLE; + goto out; } break; default: @@ -3676,29 +3554,20 @@ static CURLcode create_conn(struct Curl_easy *data, } } - if(!connections_available) { - Curl_conn_free(data, conn); - *in_connect = NULL; - - result = CURLE_NO_CONNECTION_AVAILABLE; + /* Convert needle into a full connection by filling in all the + * remaining parts like the cloned SSL configuration. */ + result = Curl_ssl_conn_config_init(data, needle); + if(result) { + DEBUGF(curl_mfprintf(stderr, "Error: init connection ssl config\n")); goto out; } - else { - /* - * This is a brand new connection, so let's store it in the connection - * cache of ours! - */ - result = Curl_ssl_conn_config_init(data, conn); - if(result) { - DEBUGF(curl_mfprintf(stderr, "Error: init connection ssl config\n")); - goto out; - } + /* attach it and no longer own it */ + Curl_attach_connection(data, needle); + needle = NULL; - Curl_attach_connection(data, conn); - result = Curl_cpool_add(data, conn); - if(result) - goto out; - } + result = Curl_cpool_add(data, data->conn); + if(result) + goto out; #ifdef USE_NTLM /* If NTLM is requested in a part of this connection, make sure we do not @@ -3721,43 +3590,34 @@ static CURLcode create_conn(struct Curl_easy *data, } /* Setup and init stuff before DO starts, in preparing for the transfer. */ - result = Curl_init_do(data, conn); + result = Curl_init_do(data, data->conn); if(result) goto out; - /* - * Setup whatever necessary for a resumed transfer - */ + /* Setup whatever necessary for a resumed transfer */ result = setup_range(data); if(result) goto out; - /* Continue connectdata initialization here. */ - - if(conn->bits.reuse) { - /* We are reusing the connection - no need to resolve anything, and - idnconvert_hostname() was called already in create_conn() for the reuse - case. */ - *reusedp = TRUE; - } - /* persist the scheme and handler the transfer is using */ - data->info.conn_scheme = conn->handler->scheme; + data->info.conn_scheme = data->conn->scheme->name; /* conn_protocol can only provide "old" protocols */ - data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; + data->info.conn_protocol = (data->conn->scheme->protocol) & CURLPROTO_MASK; data->info.used_proxy = #ifdef CURL_DISABLE_PROXY 0 #else - conn->bits.proxy + data->conn->bits.proxy #endif ; - /* Everything general done, inform filters that they need - * to prepare for a data transfer. */ + /* Lastly, inform connection filters that a new transfer is attached */ result = Curl_conn_ev_data_setup(data); out: + if(needle) + Curl_conn_free(data, needle); + DEBUGASSERT(result || data->conn); return result; } @@ -3793,7 +3653,6 @@ CURLcode Curl_connect(struct Curl_easy *data, { CURLcode result; struct connectdata *conn; - bool reused = FALSE; *asyncp = FALSE; /* assume synchronous resolves by default */ *protocol_done = FALSE; @@ -3801,8 +3660,9 @@ CURLcode Curl_connect(struct Curl_easy *data, /* Set the request to virgin state based on transfer settings */ Curl_req_hard_reset(&data->req, data); - /* call the stuff that needs to be called */ - result = create_conn(data, &conn, &reused); + /* Get or create a connection for the transfer. */ + result = url_find_or_create_conn(data); + conn = data->conn; if(result == CURLE_NO_CONNECTION_AVAILABLE) { DEBUGASSERT(!conn); @@ -3812,12 +3672,12 @@ CURLcode Curl_connect(struct Curl_easy *data, if(!result) { DEBUGASSERT(conn); Curl_pgrsTime(data, TIMER_POSTQUEUE); - if(reused) { + if(conn->bits.reuse) { if(conn->attached_xfers > 1) /* multiplexed */ *protocol_done = TRUE; } - else if(conn->handler->flags & PROTOPT_NONETWORK) { + else if(conn->scheme->flags & PROTOPT_NONETWORK) { *asyncp = FALSE; Curl_pgrsTime(data, TIMER_NAMELOOKUP); *protocol_done = TRUE; @@ -3868,7 +3728,7 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) use */ /* if the protocol used does not support wildcards, switch it off */ if(data->state.wildcardmatch && - !(conn->handler->flags & PROTOPT_WILDCARD)) + !(conn->scheme->flags & PROTOPT_WILDCARD)) data->state.wildcardmatch = FALSE; } diff --git a/vendor/curl/lib/url.h b/vendor/curl/lib/url.h index 3a8d57c..c4cd762 100644 --- a/vendor/curl/lib/url.h +++ b/vendor/curl/lib/url.h @@ -35,15 +35,17 @@ void Curl_init_userdefined(struct Curl_easy *data); void Curl_freeset(struct Curl_easy *data); CURLcode Curl_uc_to_curlcode(CURLUcode uc); -CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */ -CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect); +CURLcode Curl_close(struct Curl_easy **datap); /* opposite of Curl_open() */ +CURLcode Curl_connect(struct Curl_easy *data, + bool *asyncp, + bool *protocol_done); CURLcode Curl_setup_conn(struct Curl_easy *data, struct Curl_dns_entry *dns, bool *protocol_done); void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_parse_login_details(const char *login, const size_t len, - char **userptr, char **passwdptr, - char **optionsptr); + char **userp, char **passwdp, + char **optionsp); /* Attach/Clear/Get meta data for an easy handle. Needs to provide * a destructor, will be automatically called when the easy handle @@ -71,9 +73,8 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key); * @param scheme URI scheme, case-insensitive * @return NULL of handler not found */ -const struct Curl_handler *Curl_get_scheme_handler(const char *scheme); -const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, - size_t len); +const struct Curl_scheme *Curl_get_scheme(const char *scheme); +const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len); #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless @@ -81,7 +82,6 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, /** * Return TRUE iff the given connection is considered dead. - * @param nowp NULL or pointer to time being checked against. */ bool Curl_conn_seems_dead(struct connectdata *conn, struct Curl_easy *data); diff --git a/vendor/curl/lib/urlapi-int.h b/vendor/curl/lib/urlapi-int.h index fbce183..29d4fe5 100644 --- a/vendor/curl/lib/urlapi-int.h +++ b/vendor/curl/lib/urlapi-int.h @@ -37,4 +37,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, bool has_scheme); #endif +#define U_CURLU_URLDECODE (unsigned int)CURLU_URLDECODE +#define U_CURLU_PATH_AS_IS (unsigned int)CURLU_PATH_AS_IS + #endif /* HEADER_CURL_URLAPI_INT_H */ diff --git a/vendor/curl/lib/urlapi.c b/vendor/curl/lib/urlapi.c index e974783..a4b82f3 100644 --- a/vendor/curl/lib/urlapi.c +++ b/vendor/curl/lib/urlapi.c @@ -30,17 +30,17 @@ #include "escape.h" #include "curlx/inet_pton.h" #include "curlx/inet_ntop.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "idn.h" #include "curlx/strparse.h" #include "curl_memrchr.h" #ifdef _WIN32 /* MS-DOS/Windows style drive prefix, eg c: in c:foo */ -#define STARTS_WITH_DRIVE_PREFIX(str) \ - ((('a' <= str[0] && str[0] <= 'z') || \ - ('A' <= str[0] && str[0] <= 'Z')) && \ - (str[1] == ':')) +#define STARTS_WITH_DRIVE_PREFIX(str) \ + ((('a' <= (str)[0] && (str)[0] <= 'z') || \ + ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ + ((str)[1] == ':')) #endif /* MS-DOS/Windows style drive prefix, optionally with @@ -83,9 +83,6 @@ struct Curl_URL { #define DEFAULT_SCHEME "https" -static CURLUcode parseurl_and_replace(const char *url, CURLU *u, - unsigned int flags); - static void free_urlhandle(struct Curl_URL *u) { curlx_free(u->scheme); @@ -121,8 +118,8 @@ static const char *find_host_sep(const char *url) } /* convert CURLcode to CURLUcode */ -#define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : \ - CURLUE_OUT_OF_MEMORY) +#define cc2cu(x) \ + ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : CURLUE_OUT_OF_MEMORY) /* urlencode_str() writes data into an output dynbuf and URL-encodes the * spaces in the source URL accordingly. @@ -222,81 +219,6 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, return 0; } -/* - * Concatenate a relative URL onto a base URL making it absolute. - */ -static CURLUcode redirect_url(const char *base, const char *relurl, - CURLU *u, unsigned int flags) -{ - struct dynbuf urlbuf; - bool host_changed = FALSE; - const char *useurl = relurl; - const char *cutoff = NULL; - size_t prelen; - CURLUcode uc; - - /* protsep points to the start of the hostname, after [scheme]:// */ - const char *protsep = base + strlen(u->scheme) + 3; - DEBUGASSERT(base && relurl && u); /* all set here */ - if(!base) - return CURLUE_MALFORMED_INPUT; /* should never happen */ - - /* handle different relative URL types */ - switch(relurl[0]) { - case '/': - if(relurl[1] == '/') { - /* protocol-relative URL: //example.com/path */ - cutoff = protsep; - useurl = &relurl[2]; - host_changed = TRUE; - } - else - /* absolute /path */ - cutoff = strchr(protsep, '/'); - break; - - case '#': - /* fragment-only change */ - if(u->fragment) - cutoff = strchr(protsep, '#'); - break; - - default: - /* path or query-only change */ - if(u->query && u->query[0]) - /* remove existing query */ - cutoff = strchr(protsep, '?'); - else if(u->fragment && u->fragment[0]) - /* Remove existing fragment */ - cutoff = strchr(protsep, '#'); - - if(relurl[0] != '?') { - /* append a relative path after the last slash */ - cutoff = memrchr(protsep, '/', - cutoff ? (size_t)(cutoff - protsep) : strlen(protsep)); - if(cutoff) - cutoff++; /* truncate after last slash */ - } - break; - } - - prelen = cutoff ? (size_t)(cutoff - base) : strlen(base); - - /* build new URL */ - curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH); - - if(!curlx_dyn_addn(&urlbuf, base, prelen) && - !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) { - uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u, - flags & ~CURLU_PATH_AS_IS); - } - else - uc = CURLUE_OUT_OF_MEMORY; - - curlx_dyn_free(&urlbuf); - return uc; -} - /* scan for byte values <= 31, 127 and sometimes space */ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace) { @@ -334,7 +256,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, char *userp = NULL; char *passwdp = NULL; char *optionsp = NULL; - const struct Curl_handler *h = NULL; + const struct Curl_scheme *h = NULL; /* At this point, we assume all the other special cases have been taken * care of, so the host is at most @@ -343,7 +265,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, * * We need somewhere to put the embedded details, so do that first. */ - char *ptr; + const char *ptr; DEBUGASSERT(login); @@ -359,7 +281,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, /* if this is a known scheme, get some details */ if(u->scheme) - h = Curl_get_scheme_handler(u->scheme); + h = Curl_get_scheme(u->scheme); /* We could use the login information in the URL so extract it. Only parse options if the handler says we should. Note that 'h' might be NULL! */ @@ -414,7 +336,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, bool has_scheme) { const char *portptr; - char *hostname = curlx_dyn_ptr(host); + const char *hostname = curlx_dyn_ptr(host); /* * Find the end of an IPv6 address on the ']' ending bracket. */ @@ -439,8 +361,8 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, size_t keep = portptr - hostname; /* Browser behavior adaptation. If there is a colon with no digits after, - just cut off the name there which makes us ignore the colon and just - use the default port. Firefox, Chrome and Safari all do that. + cut off the name there which makes us ignore the colon and use the + default port. Firefox, Chrome and Safari all do that. Do not do it if the URL has no scheme, to make something that looks like a scheme not work! @@ -552,7 +474,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, * Returns the host type. */ -#define HOST_ERROR -1 /* out of memory */ +#define HOST_ERROR (-1) /* out of memory */ #define HOST_NAME 1 #define HOST_IPV4 2 @@ -655,7 +577,7 @@ static int ipv4_normalize(struct dynbuf *host) /* if necessary, replace the host content with a URL decoded version */ static CURLUcode urldecode_host(struct dynbuf *host) { - char *per = NULL; + const char *per; const char *hostname = curlx_dyn_ptr(host); per = strchr(hostname, '%'); if(!per) @@ -858,8 +780,8 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) /* remove the last segment from the output buffer */ size_t len = curlx_dyn_len(&out); if(len) { - char *ptr = curlx_dyn_ptr(&out); - char *last = memrchr(ptr, '/', len); + const char *ptr = curlx_dyn_ptr(&out); + const char *last = memrchr(ptr, '/', len); if(last) /* trim the output at the slash */ curlx_dyn_setlen(&out, last - ptr); @@ -898,353 +820,371 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) return result ? 1 : 0; /* success */ } -static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) +static CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, + struct dynbuf *host, const char **pathp, + size_t *pathlenp) { const char *path; size_t pathlen; - char *query = NULL; - char *fragment = NULL; - char schemebuf[MAX_SCHEME_LEN + 1]; - size_t schemelen = 0; - size_t urllen; - CURLUcode result = CURLUE_OK; - size_t fraglen = 0; - struct dynbuf host; - - DEBUGASSERT(url); - - curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH); + bool uncpath = FALSE; + if(urllen <= 6) + /* file:/ is not enough to actually be a complete file: URL */ + return CURLUE_BAD_FILE_URL; - result = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE)); - if(result) - goto fail; - - schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf), - flags & (CURLU_GUESS_SCHEME | - CURLU_DEFAULT_SCHEME)); - - /* handle the file: scheme */ - if(schemelen && !strcmp(schemebuf, "file")) { - bool uncpath = FALSE; - if(urllen <= 6) { - /* file:/ is not enough to actually be a complete file: URL */ - result = CURLUE_BAD_FILE_URL; - goto fail; - } + /* path has been allocated large enough to hold this */ + path = &url[5]; + pathlen = urllen - 5; - /* path has been allocated large enough to hold this */ - path = &url[5]; - pathlen = urllen - 5; + u->scheme = curlx_strdup("file"); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; - u->scheme = curlx_strdup("file"); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + /* Extra handling URLs with an authority component (i.e. that start with + * "file://") + * + * We allow omitted hostname (e.g. file:/) -- valid according to + * RFC 8089, but not the (current) WHAT-WG URL spec. + */ + if(path[0] == '/' && path[1] == '/') { + /* swallow the two slashes */ + const char *ptr = &path[2]; - /* Extra handling URLs with an authority component (i.e. that start with - * "file://") + /* + * According to RFC 8089, a file: URL can be reliably dereferenced if: + * + * o it has no/blank hostname, or * - * We allow omitted hostname (e.g. file:/) -- valid according to - * RFC 8089, but not the (current) WHAT-WG URL spec. + * o the hostname matches "localhost" (case-insensitively), or + * + * o the hostname is a FQDN that resolves to this machine, or + * + * o it is an UNC String transformed to an URI (Windows only, RFC 8089 + * Appendix E.3). + * + * For brevity, we only consider URLs with empty, "localhost", or + * "127.0.0.1" hostnames as local, otherwise as an UNC String. + * + * Additionally, there is an exception for URLs with a Windows drive + * letter in the authority (which was accidentally omitted from RFC 8089 + * Appendix E, but believe me, it was meant to be there. --MK) */ - if(path[0] == '/' && path[1] == '/') { - /* swallow the two slashes */ - const char *ptr = &path[2]; - - /* - * According to RFC 8089, a file: URL can be reliably dereferenced if: - * - * o it has no/blank hostname, or - * - * o the hostname matches "localhost" (case-insensitively), or - * - * o the hostname is a FQDN that resolves to this machine, or - * - * o it is an UNC String transformed to an URI (Windows only, RFC 8089 - * Appendix E.3). - * - * For brevity, we only consider URLs with empty, "localhost", or - * "127.0.0.1" hostnames as local, otherwise as an UNC String. - * - * Additionally, there is an exception for URLs with a Windows drive - * letter in the authority (which was accidentally omitted from RFC 8089 - * Appendix E, but believe me, it was meant to be there. --MK) - */ - if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { - /* the URL includes a hostname, it must match "localhost" or - "127.0.0.1" to be valid */ - if(checkprefix("localhost/", ptr) || - checkprefix("127.0.0.1/", ptr)) { - ptr += 9; /* now points to the slash after the host */ - } - else { + if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { + /* the URL includes a hostname, it must match "localhost" or + "127.0.0.1" to be valid */ + if(checkprefix("localhost/", ptr) || + checkprefix("127.0.0.1/", ptr)) { + ptr += 9; /* now points to the slash after the host */ + } + else { #ifdef _WIN32 - size_t len; - - /* the hostname, NetBIOS computer name, can not contain disallowed - chars, and the delimiting slash character must be appended to the - hostname */ - path = strpbrk(ptr, "/\\:*?\"<>|"); - if(!path || *path != '/') { - result = CURLUE_BAD_FILE_URL; - goto fail; - } - - len = path - ptr; - if(len) { - CURLcode code = curlx_dyn_addn(&host, ptr, len); - if(code) { - result = cc2cu(code); - goto fail; - } - uncpath = TRUE; - } + size_t len; + + /* the hostname, NetBIOS computer name, can not contain disallowed + chars, and the delimiting slash character must be appended to the + hostname */ + path = strpbrk(ptr, "/\\:*?\"<>|"); + if(!path || *path != '/') + return CURLUE_BAD_FILE_URL; + + len = path - ptr; + if(len) { + CURLcode code = curlx_dyn_addn(host, ptr, len); + if(code) + return cc2cu(code); + uncpath = TRUE; + } - ptr -= 2; /* now points to the // before the host in UNC */ + ptr -= 2; /* now points to the // before the host in UNC */ #else - /* Invalid file://hostname/, expected localhost or 127.0.0.1 or - none */ - result = CURLUE_BAD_FILE_URL; - goto fail; + /* Invalid file://hostname/, expected localhost or 127.0.0.1 or + none */ + return CURLUE_BAD_FILE_URL; #endif - } } - - path = ptr; - pathlen = urllen - (ptr - url); } - if(!uncpath) - /* no host for file: URLs by default */ - curlx_dyn_reset(&host); + path = ptr; + pathlen = urllen - (ptr - url); + } + + if(!uncpath) + /* no host for file: URLs by default */ + curlx_dyn_reset(host); #if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__) - /* Do not allow Windows drive letters when not in Windows. - * This catches both "file:/c:" and "file:c:" */ - if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || - STARTS_WITH_URL_DRIVE_PREFIX(path)) { - /* File drive letters are only accepted in MS-DOS/Windows */ - result = CURLUE_BAD_FILE_URL; - goto fail; - } + /* Do not allow Windows drive letters when not in Windows. + * This catches both "file:/c:" and "file:c:" */ + if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || + STARTS_WITH_URL_DRIVE_PREFIX(path)) { + /* File drive letters are only accepted in MS-DOS/Windows */ + return CURLUE_BAD_FILE_URL; + } #else - /* If the path starts with a slash and a drive letter, ditch the slash */ - if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { - /* This cannot be done with strcpy, as the memory chunks overlap! */ - path++; - pathlen--; - } + /* If the path starts with a slash and a drive letter, ditch the slash */ + if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { + /* This cannot be done with strcpy, as the memory chunks overlap! */ + path++; + pathlen--; + } #endif + *pathp = path; + *pathlenp = pathlen; + return CURLUE_OK; +} + +static CURLUcode parse_scheme(const char *url, CURLU *u, char *schemebuf, + size_t schemelen, unsigned int flags, + const char **hostpp) +{ + /* clear path */ + const char *schemep = NULL; + + if(schemelen) { + int i = 0; + const char *p = &url[schemelen + 1]; + while((*p == '/') && (i < 4)) { + p++; + i++; + } + + schemep = schemebuf; + if(!Curl_get_scheme(schemep) && + !(flags & CURLU_NON_SUPPORT_SCHEME)) + return CURLUE_UNSUPPORTED_SCHEME; + + if((i < 1) || (i > 3)) + /* less than one or more than three slashes */ + return CURLUE_BAD_SLASHES; + + *hostpp = p; /* hostname starts here */ } else { - /* clear path */ - const char *schemep = NULL; - const char *hostp; - size_t hostlen; + /* no scheme! */ - if(schemelen) { - int i = 0; - const char *p = &url[schemelen + 1]; - while((*p == '/') && (i < 4)) { - p++; - i++; - } - - schemep = schemebuf; - if(!Curl_get_scheme_handler(schemep) && - !(flags & CURLU_NON_SUPPORT_SCHEME)) { - result = CURLUE_UNSUPPORTED_SCHEME; - goto fail; - } + if(!(flags & (CURLU_DEFAULT_SCHEME | CURLU_GUESS_SCHEME))) + return CURLUE_BAD_SCHEME; - if((i < 1) || (i > 3)) { - /* less than one or more than three slashes */ - result = CURLUE_BAD_SLASHES; - goto fail; - } - hostp = p; /* hostname starts here */ - } - else { - /* no scheme! */ + if(flags & CURLU_DEFAULT_SCHEME) + schemep = DEFAULT_SCHEME; - if(!(flags & (CURLU_DEFAULT_SCHEME | CURLU_GUESS_SCHEME))) { - result = CURLUE_BAD_SCHEME; - goto fail; - } - if(flags & CURLU_DEFAULT_SCHEME) - schemep = DEFAULT_SCHEME; + /* + * The URL was badly formatted, let's try without scheme specified. + */ + *hostpp = url; + } - /* - * The URL was badly formatted, let's try without scheme specified. - */ - hostp = url; - } + if(schemep) { + u->scheme = curlx_strdup(schemep); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + } + return CURLUE_OK; +} - if(schemep) { - u->scheme = curlx_strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } +static CURLUcode guess_scheme(CURLU *u, struct dynbuf *host) +{ + const char *hostname = curlx_dyn_ptr(host); + const char *schemep = NULL; + /* legacy curl-style guess based on hostname */ + if(checkprefix("ftp.", hostname)) + schemep = "ftp"; + else if(checkprefix("dict.", hostname)) + schemep = "dict"; + else if(checkprefix("ldap.", hostname)) + schemep = "ldap"; + else if(checkprefix("imap.", hostname)) + schemep = "imap"; + else if(checkprefix("smtp.", hostname)) + schemep = "smtp"; + else if(checkprefix("pop3.", hostname)) + schemep = "pop3"; + else + schemep = "http"; - /* find the end of the hostname + port number */ - hostlen = strcspn(hostp, "/?#"); - path = &hostp[hostlen]; + u->scheme = curlx_strdup(schemep); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; - /* this pathlen also contains the query and the fragment */ - pathlen = urllen - (path - url); - if(hostlen) { + u->guessed_scheme = TRUE; + return CURLUE_OK; +} - result = parse_authority(u, hostp, hostlen, flags, &host, schemelen); +static CURLUcode handle_fragment(CURLU *u, const char *fragment, + size_t fraglen, unsigned int flags) +{ + CURLUcode result; + u->fragment_present = TRUE; + if(fraglen > 1) { + /* skip the leading '#' in the copy but include the terminating null */ + if(flags & CURLU_URLENCODE) { + struct dynbuf enc; + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE); if(result) - goto fail; - - if((flags & CURLU_GUESS_SCHEME) && !schemep) { - const char *hostname = curlx_dyn_ptr(&host); - /* legacy curl-style guess based on hostname */ - if(checkprefix("ftp.", hostname)) - schemep = "ftp"; - else if(checkprefix("dict.", hostname)) - schemep = "dict"; - else if(checkprefix("ldap.", hostname)) - schemep = "ldap"; - else if(checkprefix("imap.", hostname)) - schemep = "imap"; - else if(checkprefix("smtp.", hostname)) - schemep = "smtp"; - else if(checkprefix("pop3.", hostname)) - schemep = "pop3"; - else - schemep = "http"; - - u->scheme = curlx_strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - u->guessed_scheme = TRUE; - } - } - else if(flags & CURLU_NO_AUTHORITY) { - /* allowed to be empty. */ - if(curlx_dyn_add(&host, "")) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + return result; + u->fragment = curlx_dyn_ptr(&enc); } else { - result = CURLUE_NO_HOST; - goto fail; - } - } - - fragment = strchr(path, '#'); - if(fragment) { - fraglen = pathlen - (fragment - path); - u->fragment_present = TRUE; - if(fraglen > 1) { - /* skip the leading '#' in the copy but include the terminating null */ - if(flags & CURLU_URLENCODE) { - struct dynbuf enc; - curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE); - if(result) - goto fail; - u->fragment = curlx_dyn_ptr(&enc); - } - else { - u->fragment = Curl_memdup0(fragment + 1, fraglen - 1); - if(!u->fragment) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } + u->fragment = curlx_memdup0(fragment + 1, fraglen - 1); + if(!u->fragment) + return CURLUE_OUT_OF_MEMORY; } - /* after this, pathlen still contains the query */ - pathlen -= fraglen; } + return CURLUE_OK; +} - query = memchr(path, '?', pathlen); - if(query) { - size_t qlen = fragment ? (size_t)(fragment - query) : - pathlen - (query - path); - pathlen -= qlen; - u->query_present = TRUE; - if(qlen > 1) { - if(flags & CURLU_URLENCODE) { - struct dynbuf enc; - curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - /* skip the leading question mark */ - result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE); - if(result) - goto fail; - u->query = curlx_dyn_ptr(&enc); - } - else { - u->query = Curl_memdup0(query + 1, qlen - 1); - if(!u->query) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } +static CURLUcode handle_query(CURLU *u, const char *query, + size_t qlen, unsigned int flags) +{ + u->query_present = TRUE; + if(qlen > 1) { + if(flags & CURLU_URLENCODE) { + struct dynbuf enc; + CURLUcode result; + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + /* skip the leading question mark */ + result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE); + if(result) + return result; + u->query = curlx_dyn_ptr(&enc); } else { - /* single byte query */ - u->query = curlx_strdup(""); - if(!u->query) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + u->query = curlx_memdup0(query + 1, qlen - 1); + if(!u->query) + return CURLUE_OUT_OF_MEMORY; } } + else { + /* single byte query */ + u->query = curlx_strdup(""); + if(!u->query) + return CURLUE_OUT_OF_MEMORY; + } + return CURLUE_OK; +} +static CURLUcode handle_path(CURLU *u, const char *path, + size_t pathlen, unsigned int flags) +{ + CURLUcode result; if(pathlen && (flags & CURLU_URLENCODE)) { struct dynbuf enc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); result = urlencode_str(&enc, path, pathlen, TRUE, FALSE); if(result) - goto fail; + return result; pathlen = curlx_dyn_len(&enc); path = u->path = curlx_dyn_ptr(&enc); } if(pathlen <= 1) { - /* there is no path left or just the slash, unset */ + /* there is no path left or the slash, unset */ path = NULL; } else { if(!u->path) { - u->path = Curl_memdup0(path, pathlen); - if(!u->path) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + u->path = curlx_memdup0(path, pathlen); + if(!u->path) + return CURLUE_OUT_OF_MEMORY; path = u->path; } else if(flags & CURLU_URLENCODE) - /* it might have encoded more than just the path so cut it */ + /* it might have encoded more than the path so cut it */ u->path[pathlen] = 0; if(!(flags & CURLU_PATH_AS_IS)) { /* remove ../ and ./ sequences according to RFC3986 */ char *dedot; int err = dedotdotify(path, pathlen, &dedot); - if(err) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + if(err) + return CURLUE_OUT_OF_MEMORY; if(dedot) { curlx_free(u->path); u->path = dedot; } } } + return CURLUE_OK; +} + +static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) +{ + const char *path; + size_t pathlen; + char schemebuf[MAX_SCHEME_LEN + 1]; + size_t schemelen = 0; + size_t urllen; + CURLUcode result = CURLUE_OK; + struct dynbuf host; + + DEBUGASSERT(url); - u->host = curlx_dyn_ptr(&host); + curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - return result; + result = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE)); + if(result) + goto fail; + + schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf), + flags & (CURLU_GUESS_SCHEME | + CURLU_DEFAULT_SCHEME)); + + /* handle the file: scheme */ + if(schemelen && !strcmp(schemebuf, "file")) + result = parse_file(url, urllen, u, &host, &path, &pathlen); + else { + const char *hostp = NULL; + size_t hostlen; + result = parse_scheme(url, u, schemebuf, schemelen, flags, &hostp); + if(result) + goto fail; + + /* find the end of the hostname + port number */ + hostlen = strcspn(hostp, "/?#"); + path = &hostp[hostlen]; + + /* this pathlen also contains the query and the fragment */ + pathlen = urllen - (path - url); + if(hostlen) { + result = parse_authority(u, hostp, hostlen, flags, &host, + u->scheme != NULL); + if(!result && (flags & CURLU_GUESS_SCHEME) && !u->scheme) + result = guess_scheme(u, &host); + } + else if(flags & CURLU_NO_AUTHORITY) { + /* allowed to be empty. */ + if(curlx_dyn_add(&host, "")) + result = CURLUE_OUT_OF_MEMORY; + } + else + result = CURLUE_NO_HOST; + } + if(!result) { + /* The path might at this point contain a fragment and/or a query to + handle */ + const char *fragment = strchr(path, '#'); + if(fragment) { + size_t fraglen = pathlen - (fragment - path); + result = handle_fragment(u, fragment, fraglen, flags); + /* after this, pathlen still contains the query */ + pathlen -= fraglen; + } + } + if(!result) { + const char *query = memchr(path, '?', pathlen); + if(query) { + size_t qlen = pathlen - (query - path); + result = handle_query(u, query, qlen, flags); + pathlen -= qlen; + } + } + if(!result) + /* the fragment and query parts are trimmed off from the path */ + result = handle_path(u, path, pathlen, flags); + if(!result) { + u->host = curlx_dyn_ptr(&host); + return CURLUE_OK; + } fail: curlx_dyn_free(&host); free_urlhandle(u); @@ -1268,6 +1208,81 @@ static CURLUcode parseurl_and_replace(const char *url, CURLU *u, return result; } +/* + * Concatenate a relative URL onto a base URL making it absolute. + */ +static CURLUcode redirect_url(const char *base, const char *relurl, + CURLU *u, unsigned int flags) +{ + struct dynbuf urlbuf; + bool host_changed = FALSE; + const char *useurl = relurl; + const char *cutoff = NULL; + size_t prelen; + CURLUcode uc; + + /* protsep points to the start of the hostname, after [scheme]:// */ + const char *protsep = base + strlen(u->scheme) + 3; + DEBUGASSERT(base && relurl && u); /* all set here */ + if(!base) + return CURLUE_MALFORMED_INPUT; /* should never happen */ + + /* handle different relative URL types */ + switch(relurl[0]) { + case '/': + if(relurl[1] == '/') { + /* protocol-relative URL: //example.com/path */ + cutoff = protsep; + useurl = &relurl[2]; + host_changed = TRUE; + } + else + /* absolute /path */ + cutoff = strchr(protsep, '/'); + break; + + case '#': + /* fragment-only change */ + if(u->fragment) + cutoff = strchr(protsep, '#'); + break; + + default: + /* path or query-only change */ + if(u->query && u->query[0]) + /* remove existing query */ + cutoff = strchr(protsep, '?'); + else if(u->fragment && u->fragment[0]) + /* Remove existing fragment */ + cutoff = strchr(protsep, '#'); + + if(relurl[0] != '?') { + /* append a relative path after the last slash */ + cutoff = memrchr(protsep, '/', + cutoff ? (size_t)(cutoff - protsep) : strlen(protsep)); + if(cutoff) + cutoff++; /* truncate after last slash */ + } + break; + } + + prelen = cutoff ? (size_t)(cutoff - base) : strlen(base); + + /* build new URL */ + curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH); + + if(!curlx_dyn_addn(&urlbuf, base, prelen) && + !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) { + uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u, + flags & ~U_CURLU_PATH_AS_IS); + } + else + uc = CURLUE_OUT_OF_MEMORY; + + curlx_dyn_free(&urlbuf); + return uc; +} + /* */ CURLU *curl_url(void) @@ -1285,9 +1300,9 @@ void curl_url_cleanup(CURLU *u) #define DUP(dest, src, name) \ do { \ - if(src->name) { \ - dest->name = curlx_strdup(src->name); \ - if(!dest->name) \ + if((src)->name) { \ + (dest)->name = curlx_strdup((src)->name); \ + if(!(dest)->name) \ goto fail; \ } \ } while(0) @@ -1349,7 +1364,7 @@ static CURLUcode urlget_format(const CURLU *u, CURLUPart what, bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; bool punycode = (flags & CURLU_PUNYCODE) && (what == CURLUPART_HOST); bool depunyfy = (flags & CURLU_PUNY2IDN) && (what == CURLUPART_HOST); - char *part = Curl_memdup0(ptr, partlen); + char *part = curlx_memdup0(ptr, partlen); *partp = NULL; if(!part) return CURLUE_OUT_OF_MEMORY; @@ -1425,15 +1440,15 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) if(u->scheme && curl_strequal("file", u->scheme)) { url = curl_maprintf("file://%s%s%s%s%s", u->path, - show_query ? "?": "", + show_query ? "?" : "", u->query ? u->query : "", - show_fragment ? "#": "", + show_fragment ? "#" : "", u->fragment ? u->fragment : ""); } else if(!u->host) return CURLUE_NO_HOST; else { - const struct Curl_handler *h = NULL; + const struct Curl_scheme *h = NULL; char schemebuf[MAX_SCHEME_LEN + 5]; if(u->scheme) scheme = u->scheme; @@ -1442,7 +1457,7 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) else return CURLUE_NO_SCHEME; - h = Curl_get_scheme_handler(scheme); + h = Curl_get_scheme(scheme); if(!port && (flags & CURLU_DEFAULT_PORT)) { /* there is no stored port number, but asked to deliver a default one for the scheme */ @@ -1502,18 +1517,18 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) url = curl_maprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", schemebuf, u->user ? u->user : "", - u->password ? ":": "", + u->password ? ":" : "", u->password ? u->password : "", options ? ";" : "", options ? options : "", - (u->user || u->password || options) ? "@": "", + (u->user || u->password || options) ? "@" : "", allochost ? allochost : u->host, - port ? ":": "", + port ? ":" : "", port ? port : "", u->path ? u->path : "/", - show_query ? "?": "", + show_query ? "?" : "", u->query ? u->query : "", - show_fragment ? "#": "", + show_fragment ? "#" : "", u->fragment ? u->fragment : ""); curlx_free(allochost); } @@ -1540,7 +1555,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, case CURLUPART_SCHEME: ptr = u->scheme; ifmissing = CURLUE_NO_SCHEME; - flags &= ~CURLU_URLDECODE; /* never for schemes */ + flags &= ~U_CURLU_URLDECODE; /* never for schemes */ if((flags & CURLU_NO_GUESS_SCHEME) && u->guessed_scheme) return CURLUE_NO_SCHEME; break; @@ -1567,11 +1582,11 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, case CURLUPART_PORT: ptr = u->port; ifmissing = CURLUE_NO_PORT; - flags &= ~CURLU_URLDECODE; /* never for port */ + flags &= ~U_CURLU_URLDECODE; /* never for port */ if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { /* there is no stored port number, but asked to deliver a default one for the scheme */ - const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); + const struct Curl_scheme *h = Curl_get_scheme(u->scheme); if(h) { curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); ptr = portbuf; @@ -1580,7 +1595,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, else if(ptr && u->scheme) { /* there is a stored port number, but ask to inhibit if it matches the default one for the scheme */ - const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); + const struct Curl_scheme *h = Curl_get_scheme(u->scheme); if(h && (h->defport == u->portnum) && (flags & CURLU_NO_DEFAULT_PORT)) ptr = NULL; @@ -1622,16 +1637,16 @@ static CURLUcode set_url_scheme(CURLU *u, const char *scheme, unsigned int flags) { size_t plen = strlen(scheme); - const struct Curl_handler *h = NULL; + const struct Curl_scheme *h = NULL; if((plen > MAX_SCHEME_LEN) || (plen < 1)) /* too long or too short */ return CURLUE_BAD_SCHEME; /* verify that it is a fine scheme */ - h = Curl_get_scheme_handler(scheme); + h = Curl_get_scheme(scheme); + if(!(flags & CURLU_NON_SUPPORT_SCHEME) && (!h || !h->run)) + return CURLUE_UNSUPPORTED_SCHEME; if(!h) { const char *s = scheme; - if(!(flags & CURLU_NON_SUPPORT_SCHEME)) - return CURLUE_UNSUPPORTED_SCHEME; if(ISALPHA(*s)) { /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ while(--plen) { @@ -1862,7 +1877,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, { const char *newp; struct dynbuf enc; - curlx_dyn_init(&enc, nalloc * 3 + 1 + leadingslash); + curlx_dyn_init(&enc, (nalloc * 3) + 1 + leadingslash); if(leadingslash && (part[0] != '/')) { CURLcode result = curlx_dyn_addn(&enc, "/", 1); diff --git a/vendor/curl/lib/urldata.h b/vendor/curl/lib/urldata.h index aba1693..5ae1480 100644 --- a/vendor/curl/lib/urldata.h +++ b/vendor/curl/lib/urldata.h @@ -50,16 +50,15 @@ #define PORT_RTMPS PORT_HTTPS #define PORT_GOPHER 70 #define PORT_MQTT 1883 - -struct curl_trc_featt; +#define PORT_MQTTS 8883 #ifdef USE_ECH /* CURLECH_ bits for the tls_ech option */ -# define CURLECH_DISABLE (1 << 0) -# define CURLECH_GREASE (1 << 1) -# define CURLECH_ENABLE (1 << 2) -# define CURLECH_HARD (1 << 3) -# define CURLECH_CLA_CFG (1 << 4) +#define CURLECH_DISABLE (1 << 0) +#define CURLECH_GREASE (1 << 1) +#define CURLECH_ENABLE (1 << 2) +#define CURLECH_HARD (1 << 3) +#define CURLECH_CLA_CFG (1 << 4) #endif #ifndef CURL_DISABLE_WEBSOCKETS @@ -75,23 +74,20 @@ struct curl_trc_featt; #define CURLPROTO_WSS 0L #endif +#define CURLPROTO_MQTTS (1LL << 32) + +#define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff) + /* the default protocols accepting a redirect to */ #define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \ CURLPROTO_FTPS) -/* This should be undefined once we need bit 32 or higher */ -#define PROTO_TYPE_SMALL - -#ifndef PROTO_TYPE_SMALL typedef curl_off_t curl_prot_t; -#else -typedef unsigned int curl_prot_t; -#endif /* This mask is for all the old protocols that are provided and defined in the public header and shall exclude protocols added since which are not exposed in the API */ -#define CURLPROTO_MASK (0x3ffffff) +#define CURLPROTO_MASK 0x3ffffff #define CURL_DEFAULT_USER "anonymous" #define CURL_DEFAULT_PASSWORD "ftp@example.com" @@ -131,10 +127,6 @@ typedef unsigned int curl_prot_t; input easier and better. */ #define CURL_MAX_INPUT_LENGTH 8000000 -#include "cookie.h" -#include "psl.h" -#include "formdata.h" - #ifdef HAVE_NETINET_IN_H #include #endif @@ -144,6 +136,10 @@ typedef unsigned int curl_prot_t; #include "curlx/timeval.h" +#include "asyn.h" +#include "cookie.h" +#include "psl.h" +#include "formdata.h" #include "http_chunks.h" /* for the structs and enum stuff */ #include "hostip.h" #include "hash.h" @@ -180,14 +176,14 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ #include "cf-socket.h" #ifdef HAVE_GSSAPI -# ifdef HAVE_GSSGNU -# include -# elif defined(HAVE_GSSAPI_H) -# include -# else /* MIT Kerberos */ -# include -# include /* for GSS_C_CHANNEL_BOUND_FLAG, in 1.19+ */ -# endif +# ifdef HAVE_GSSGNU +# include +# elif defined(HAVE_GSSAPI_H) +# include +# else /* MIT Kerberos */ +# include +# include /* for GSS_C_CHANNEL_BOUND_FLAG in 1.19+ */ +# endif #endif #ifdef USE_LIBSSH2 @@ -218,16 +214,12 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ * us early warning on things only discovered by valgrind otherwise. */ #define GOOD_EASY_HANDLE(x) \ (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \ - (DEBUGASSERT(!(x)), FALSE)) + (DEBUGASSERT(!(x)), FALSE)) #else #define GOOD_EASY_HANDLE(x) \ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) #endif -/* SSL backend-specific data; declared differently by each SSL backend */ -struct ssl_backend_data; -struct Curl_ssl_scache_entry; - struct ssl_primary_config { char *CApath; /* certificate directory (does not work on Windows) */ char *CAfile; /* certificate to verify peer against */ @@ -246,9 +238,9 @@ struct ssl_primary_config { char *password; /* TLS password (for, e.g., SRP) */ #endif char *curves; /* list of curves to use */ - unsigned int version_max; /* max supported version the client wants to use */ - unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ - unsigned char version; /* what version the client wants to use */ + uint32_t version_max; /* max supported version the client wants to use */ + uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ + uint8_t version; /* what version the client wants to use */ BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ BIT(verifystatus); /* set TRUE if certificate status must be checked */ @@ -307,7 +299,7 @@ struct digestdata { char *qop; char *algorithm; int nc; /* nonce count */ - unsigned char algo; + uint8_t algo; BIT(stale); /* set true for re-negotiation */ BIT(userhash); #endif @@ -333,7 +325,7 @@ typedef enum { #ifdef CURL_DISABLE_PROXY #define CONN_IS_PROXIED(x) 0 #else -#define CONN_IS_PROXIED(x) x->bits.proxy +#define CONN_IS_PROXIED(x) (x)->bits.proxy #endif /* @@ -430,9 +422,7 @@ struct hostname { * Specific protocol handler. */ -struct Curl_handler { - const char *scheme; /* URL scheme name in lowercase */ - +struct Curl_protocol { /* Complement to setup_connection_internals(). This is done before the transfer "owns" the connection. */ CURLcode (*setup_connection)(struct Curl_easy *data, @@ -505,9 +495,9 @@ struct Curl_handler { /* This function can perform various checks on the connection. See CONNCHECK_* for more information about the checks that can be performed, and CONNRESULT_* for the results that can be returned. */ - unsigned int (*connection_check)(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform); + uint32_t (*connection_check)(struct Curl_easy *data, + struct connectdata *conn, + uint32_t checks_to_perform); /* attach() attaches this transfer to this connection */ void (*attach)(struct Curl_easy *data, struct connectdata *conn); @@ -517,14 +507,17 @@ struct Curl_handler { the way the follow request is performed. */ CURLcode (*follow)(struct Curl_easy *data, const char *newurl, followtype type); +}; - uint16_t defport; /* Default port. */ +struct Curl_scheme { + const char *name; /* URL scheme name in lowercase */ + const struct Curl_protocol *run; /* implementation */ curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single specific protocol bit */ - curl_prot_t family; /* single bit for protocol family; basically the - non-TLS name of the protocol this is */ - unsigned int flags; /* Extra particular characteristics, see PROTOPT_* */ - + curl_prot_t family; /* single bit for protocol family; the non-TLS name + of the protocol this is */ + uint32_t flags; /* Extra particular characteristics, see PROTOPT_* */ + uint16_t defport; /* Default port. */ }; #define PROTOPT_NONE 0 /* nothing extra */ @@ -590,7 +583,7 @@ struct ip_quadruple { struct proxy_info { struct hostname host; uint16_t port; - unsigned char proxytype; /* what kind of proxy that is in use */ + uint8_t proxytype; /* what kind of proxy that is in use */ char *user; /* proxy username string, allocated */ char *passwd; /* proxy password string, allocated */ }; @@ -624,7 +617,6 @@ struct connectdata { struct Curl_hash meta_hash; struct hostname host; - char *hostname_resolve; /* hostname to resolve to address, allocated */ char *secondaryhostname; /* secondary socket hostname (ftp) */ struct hostname conn_to_host; /* the host to connect to. valid only if bits.conn_to_host is set */ @@ -662,8 +654,8 @@ struct connectdata { #endif struct ConnectBits bits; /* various state-flags for this connection */ - const struct Curl_handler *handler; /* Connection's protocol handler */ - const struct Curl_handler *given; /* The protocol first given */ + const struct Curl_scheme *scheme; /* Connection's protocol handler */ + const struct Curl_scheme *given; /* The protocol first given */ /* Protocols can use a custom keepalive mechanism to keep connections alive. This allows those protocols to track the last time the keepalive mechanism @@ -675,7 +667,7 @@ struct connectdata { * for concurrency reasons. That multi might run in another thread. * `attached_multi` is set by the first transfer attached and cleared * when the last one is detached. - * NEVER call anything on this multi, just check for equality. */ + * NEVER call anything on this multi, check for equality. */ struct Curl_multi *attached_multi; /*************** Request - specific items ************/ @@ -703,7 +695,7 @@ struct connectdata { that subsequent bound-requested connections are not accidentally reusing wrong connections. */ char *localdev; - unsigned short localportrange; + uint16_t localportrange; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) int socks5_gssapi_enctype; #endif @@ -715,21 +707,21 @@ struct connectdata { uint32_t attached_xfers; /* # of attached easy handles */ #ifdef USE_IPV6 - unsigned int scope_id; /* Scope id for IPv6 */ + uint32_t scope_id; /* Scope id for IPv6 */ #endif - unsigned short localport; - unsigned short secondary_port; /* secondary socket remote port to connect to + uint16_t localport; + uint16_t secondary_port; /* secondary socket remote port to connect to (ftp) */ - unsigned char transport_wanted; /* one of the TRNSPRT_* defines. Not + uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not necessarily the transport the connection ends using due to Alt-Svc and happy eyeballing. Use `Curl_conn_get_transport() for actual value once the connection is set up. */ - unsigned char ip_version; /* copied from the Curl_easy at creation time */ + uint8_t ip_version; /* copied from the Curl_easy at creation time */ /* HTTP version last responded with by the server or negotiated via ALPN. * 0 at start, then one of 09, 10, 11, etc. */ - unsigned char httpversion_seen; - unsigned char connect_only; - unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ + uint8_t httpversion_seen; + uint8_t gssapi_delegation; /* inherited from set.gssapi_delegation */ + BIT(connect_only); }; #ifndef CURL_DISABLE_PROXY @@ -757,15 +749,15 @@ struct PureInfo { time_t filetime; /* If requested, this is might get set. Set to -1 if the time was unretrievable. */ curl_off_t request_size; /* the amount of bytes sent in the request(s) */ - unsigned long proxyauthavail; /* what proxy auth types were announced */ - unsigned long httpauthavail; /* what host auth types were announced */ - unsigned long proxyauthpicked; /* selected proxy auth type */ - unsigned long httpauthpicked; /* selected host auth type */ - long numconnects; /* how many new connection did libcurl created */ + curl_off_t numconnects; /* how many new connections libcurl created */ + uint32_t proxyauthavail; /* what proxy auth types were announced */ + uint32_t httpauthavail; /* what host auth types were announced */ + uint32_t proxyauthpicked; /* selected proxy auth type */ + uint32_t httpauthpicked; /* selected host auth type */ char *contenttype; /* the content type of the object */ char *wouldredirect; /* URL this would have been redirected to if asked to */ curl_off_t retry_after; /* info from Retry-After: header */ - unsigned int header_size; /* size of read header(s) in bytes */ + uint32_t header_size; /* size of read header(s) in bytes */ /* PureInfo primary ip_quadruple is copied over from the connectdata struct in order to allow curl_easy_getinfo() to return this information @@ -778,7 +770,7 @@ struct PureInfo { number of the used URL, independent of proxy or not */ const char *conn_scheme; - unsigned int conn_protocol; + uint32_t conn_protocol; struct curl_certinfo certs; /* info about the certs. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ CURLproxycode pxcode; @@ -825,7 +817,7 @@ struct Progress { curl_off_t speed_amount[CURL_SPEED_RECORDS]; struct curltime speed_time[CURL_SPEED_RECORDS]; - unsigned char speeder_c; + uint8_t speeder_c; BIT(hide); BIT(ul_size_known); BIT(dl_size_known); @@ -851,11 +843,11 @@ typedef enum { } Curl_RtspReq; struct auth { - unsigned long want; /* Bitmask set to the authentication methods wanted by - app (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */ - unsigned long picked; - unsigned long avail; /* Bitmask for what the server reports to support for - this resource */ + uint32_t want; /* Bitmask set to the authentication methods wanted by app + (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */ + uint32_t picked; + uint32_t avail; /* Bitmask for what the server reports to support for this + resource */ BIT(done); /* TRUE when the auth phase is done and ready to do the actual request */ BIT(multipass); /* TRUE if this is not yet authenticated but within the @@ -1002,11 +994,10 @@ struct UrlState { #ifndef CURL_DISABLE_RTSP /* This RTSP state information survives requests and connections */ - long rtsp_next_client_CSeq; /* the session's next client CSeq */ - long rtsp_next_server_CSeq; /* the session's next server CSeq */ - long rtsp_CSeq_recv; /* most recent CSeq received */ - - unsigned char rtp_channel_mask[32]; /* for the correctness checking of the + uint32_t rtsp_next_client_CSeq; /* the session's next client CSeq */ + uint32_t rtsp_next_server_CSeq; /* the session's next server CSeq */ + uint32_t rtsp_CSeq_recv; /* most recent CSeq received */ + uint8_t rtp_channel_mask[32]; /* for the correctness checking of the interleaved data */ #endif @@ -1044,7 +1035,7 @@ struct UrlState { curl_easy_setopt(COOKIEFILE) calls */ #endif -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */ #endif @@ -1080,10 +1071,10 @@ struct UrlState { #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; #endif - unsigned short followlocation; /* redirect counter */ - unsigned char retrycount; /* number of retries on a new connection, up to + uint16_t followlocation; /* redirect counter */ + uint8_t retrycount; /* number of retries on a new connection, up to CONN_MAX_RETRIES */ - unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) + uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ unsigned int creds_from:2; /* where is the server credentials originating from, see the CREDS_* defines above */ @@ -1273,7 +1264,7 @@ enum dupstring { STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ - STRING_LAST /* not used, just an end-of-list marker */ + STRING_LAST /* not used, an end-of-list marker */ }; enum dupblob { @@ -1297,8 +1288,8 @@ struct UserDefined { void *out; /* CURLOPT_WRITEDATA */ void *in_set; /* CURLOPT_READDATA */ void *writeheader; /* write the header to this if non-NULL */ - unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */ - unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */ + uint32_t httpauth; /* kind of HTTP authentication to use (bitmask) */ + uint32_t proxyauth; /* kind of proxy authentication to use (bitmask) */ void *postfields; /* if POST, set the fields' values here */ curl_seek_callback seek_func; /* function that seeks the input */ curl_off_t postfieldsize; /* if POST, this might have a size to use instead @@ -1308,7 +1299,7 @@ struct UserDefined { curl_write_callback fwrite_header; /* function that stores headers */ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ curl_read_callback fread_func_set; /* function that reads the input */ - curl_progress_callback fprogress; /* OLD and deprecated progress callback */ + curl_progress_callback fprogress; /* OLD and deprecated progress callback */ curl_xferinfo_callback fxferinfo; /* progress callback */ curl_debug_callback fdebug; /* function that write informational data */ curl_ioctl_callback ioctl_func; /* function for I/O control */ @@ -1338,8 +1329,7 @@ struct UserDefined { timediff_t conn_max_age_ms; /* max time since creation to allow a connection that is to be reused */ curl_off_t filesize; /* size of file to upload, -1 means unknown */ - long low_speed_limit; /* bytes/second */ - long low_speed_time; /* number of seconds */ + curl_off_t low_speed_limit; /* bytes/second */ curl_off_t max_send_speed; /* high speed limit in bytes/second for upload */ curl_off_t max_recv_speed; /* high speed limit in bytes/second for download */ @@ -1347,7 +1337,7 @@ struct UserDefined { struct curl_slist *headers; /* linked list of extra headers */ struct curl_httppost *httppost; /* linked list of old POST data */ #if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) - curl_mimepart mimepost; /* MIME/POST data. */ + curl_mimepart *mimepostp; /* MIME/POST data. */ #endif #ifndef CURL_DISABLE_TELNET struct curl_slist *telnet_options; /* linked list of telnet options */ @@ -1364,14 +1354,14 @@ struct UserDefined { uint16_t proxyport; /* If non-zero, use this port number by default. If the proxy string features a ":[port]" that one will override this. */ - unsigned char proxytype; /* what kind of proxy */ - unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ + uint8_t proxytype; /* what kind of proxy */ + uint8_t socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ #endif struct ssl_general_config general_ssl; /* general user defined SSL stuff */ timediff_t dns_cache_timeout_ms; /* DNS cache timeout (milliseconds) */ - unsigned int buffer_size; /* size of receive buffer to use */ - unsigned int upload_buffer_size; /* size of upload buffer to use, - keep it >= CURL_MAX_WRITE_SIZE */ + uint32_t buffer_size; /* size of receive buffer to use */ + uint32_t upload_buffer_size; /* size of upload buffer to use, keep it >= + CURL_MAX_WRITE_SIZE */ void *private_data; /* application-private data */ #ifndef CURL_DISABLE_HTTP struct curl_slist *http200aliases; /* linked list of aliases for http200 */ @@ -1379,9 +1369,9 @@ struct UserDefined { curl_off_t max_filesize; /* Maximum file size to download */ #ifndef CURL_DISABLE_FTP timediff_t accepttimeout; /* in milliseconds, 0 means no timeout */ - unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile */ - unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ - unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */ + uint8_t ftp_filemethod; /* how to get to a file: curl_ftpfile */ + uint8_t ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ + uint8_t ftp_ccc; /* FTP CCC options: curl_ftpccc */ #endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) struct curl_slist *quote; /* after connection is established */ @@ -1395,14 +1385,14 @@ struct UserDefined { #ifdef USE_SSH curl_sshkeycallback ssh_keyfunc; /* key matching callback */ void *ssh_keyfunc_userp; /* custom pointer to callback */ - int ssh_auth_types; /* allowed SSH auth types */ - unsigned int new_directory_perms; /* when creating remote dirs */ + uint32_t ssh_auth_types; /* allowed SSH auth types */ + uint32_t new_directory_perms; /* when creating remote dirs */ #endif - unsigned int new_file_perms; /* when creating remote files */ + uint32_t new_file_perms; /* when creating remote files */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ struct curl_blob *blobs[BLOB_LAST]; #ifdef USE_IPV6 - unsigned int scope_id; /* Scope id for IPv6 */ + uint32_t scope_id; /* Scope id for IPv6 */ #endif curl_prot_t allowed_protocols; curl_prot_t redir_protocols; @@ -1437,7 +1427,8 @@ struct UserDefined { curl_resolver_start_callback resolver_start; /* optional callback called before resolver start */ void *resolver_start_client; /* pointer to pass to resolver start callback */ - long upkeep_interval_ms; /* Time between calls for connection upkeep. */ + timediff_t upkeep_interval_ms; /* Time between calls for connection + upkeep. */ CURLU *uh; /* URL handle for the current parsed URL */ #ifndef CURL_DISABLE_HTTP void *trailer_data; /* pointer to pass to trailer data callback */ @@ -1446,50 +1437,49 @@ struct UserDefined { #ifndef CURL_DISABLE_SMTP struct curl_slist *mail_rcpt; /* linked list of mail recipients */ #endif - unsigned int maxconnects; /* Max idle connections in the connection cache */ + uint32_t maxconnects; /* Max idle connections in the connection cache */ #ifdef USE_ECH - int tls_ech; /* TLS ECH configuration */ + int tls_ech; /* TLS ECH configuration */ #endif short maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ - unsigned short expect_100_timeout; /* in milliseconds */ - unsigned short use_port; /* which port to use (when not using default) */ + uint16_t expect_100_timeout; /* in milliseconds */ + uint16_t use_port; /* which port to use (when not using default) */ + uint16_t low_speed_time; /* number of seconds */ #ifndef CURL_DISABLE_BINDLOCAL - unsigned short localport; /* local port number to bind to */ - unsigned short localportrange; /* number of additional port numbers to test + uint16_t localport; /* local port number to bind to */ + uint16_t localportrange; /* number of additional port numbers to test in case the 'localport' one cannot be bind()ed */ #endif #ifndef CURL_DISABLE_TFTP - unsigned short tftp_blksize; /* in bytes, 0 means use default */ + uint16_t tftp_blksize; /* in bytes, 0 means use default */ #endif #ifndef CURL_DISABLE_NETRC - unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ + uint8_t use_netrc; /* enum CURL_NETRC_OPTION values */ #endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP 1 - create directories that do not exist 2 - the same but also allow MKD to fail once */ - unsigned char ftp_create_missing_dirs; -#endif - unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! (type: curl_usessl)*/ - char keep_post; /* keep POSTs as POSTs after a 30x request; each - bit represents a request, from 301 to 303 */ - unsigned char timecondition; /* kind of time comparison: curl_TimeCond */ - unsigned char method; /* what kind of HTTP request: Curl_HttpReq */ - unsigned char httpwant; /* when non-zero, a specific HTTP version requested + uint8_t ftp_create_missing_dirs; +#endif + uint8_t use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or + POP3 or others! (type: curl_usessl)*/ + uint8_t timecondition; /* kind of time comparison: curl_TimeCond */ + uint8_t method; /* what kind of HTTP request: Curl_HttpReq */ + uint8_t httpwant; /* when non-zero, a specific HTTP version requested to be used in the library's request(s) */ - unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header + uint8_t ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ - unsigned char upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ + uint8_t upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ #ifdef HAVE_GSSAPI /* GSS-API credential delegation, see the documentation of CURLOPT_GSSAPI_DELEGATION */ - unsigned char gssapi_delegation; + uint8_t gssapi_delegation; #endif - unsigned char http_follow_mode; /* follow HTTP redirects */ + uint8_t http_follow_mode; /* follow HTTP redirects */ BIT(connect_only); /* make connection/request, then let application use the socket */ BIT(connect_only_ws); /* special websocket connect-only level */ @@ -1549,7 +1539,7 @@ struct UserDefined { BIT(opt_no_body); /* as set with CURLOPT_NOBODY */ BIT(verbose); /* output verbosity */ BIT(reuse_forbid); /* forbidden to be reused, close after use */ - BIT(reuse_fresh); /* do not reuse an existing connection */ + BIT(reuse_fresh); /* do not reuse an existing connection */ BIT(no_signal); /* do not use any signal/alarm handler */ BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */ BIT(ignorecl); /* ignore content length */ @@ -1591,10 +1581,14 @@ struct UserDefined { BIT(ws_raw_mode); BIT(ws_no_auto_pong); #endif + BIT(post301); /* keep POSTs as POSTs after a 301 request */ + BIT(post302); /* keep POSTs as POSTs after a 302 request */ + BIT(post303); /* keep POSTs as POSTs after a 303 request */ }; #ifndef CURL_DISABLE_MIME -#define IS_MIME_POST(a) ((a)->set.mimepost.kind != MIMEKIND_NONE) +#define IS_MIME_POST(a) \ + ((a)->set.mimepostp && ((a)->set.mimepostp->kind != MIMEKIND_NONE)) #else #define IS_MIME_POST(a) FALSE #endif @@ -1618,7 +1612,7 @@ typedef void multi_sub_xfer_done_cb(struct Curl_easy *master_easy, struct Curl_easy { /* First a simple identifier to easier detect if a user mix up this easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ - unsigned int magic; + uint32_t magic; /* once an easy handle is tied to a connection pool a non-negative number to distinguish this transfer from other using the same pool. For easier tracking in log output. This may wrap around after LONG_MAX to 0 again, diff --git a/vendor/curl/lib/vauth/cleartext.c b/vendor/curl/lib/vauth/cleartext.c index fc8a6eb..7976ade 100644 --- a/vendor/curl/lib/vauth/cleartext.c +++ b/vendor/curl/lib/vauth/cleartext.c @@ -24,13 +24,13 @@ * Draft LOGIN SASL Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_POP3) || \ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) -#include "vauth.h" +#include "vauth/vauth.h" /* * Curl_auth_create_plain_message() @@ -81,14 +81,14 @@ CURLcode Curl_auth_create_plain_message(const char *authzid, * * Parameters: * - * valuep [in] - The username or user's password. + * value [in] - The username or user's password. * out [out] - The result storage. * * Returns void. */ -void Curl_auth_create_login_message(const char *valuep, struct bufref *out) +void Curl_auth_create_login_message(const char *value, struct bufref *out) { - Curl_bufref_set(out, valuep, strlen(valuep), NULL); + Curl_bufref_set(out, value, strlen(value), NULL); } /* diff --git a/vendor/curl/lib/vauth/cram.c b/vendor/curl/lib/vauth/cram.c index f539973..d3f2d13 100644 --- a/vendor/curl/lib/vauth/cram.c +++ b/vendor/curl/lib/vauth/cram.c @@ -23,14 +23,13 @@ * RFC2195 CRAM-MD5 authentication * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH -#include "vauth.h" -#include "../curl_hmac.h" -#include "../curl_md5.h" - +#include "vauth/vauth.h" +#include "curl_hmac.h" +#include "curl_md5.h" /* * Curl_auth_create_cram_md5_message() diff --git a/vendor/curl/lib/vauth/digest.c b/vendor/curl/lib/vauth/digest.c index a1fd248..9609390 100644 --- a/vendor/curl/lib/vauth/digest.c +++ b/vendor/curl/lib/vauth/digest.c @@ -24,18 +24,18 @@ * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH -#include "vauth.h" -#include "digest.h" -#include "../curlx/base64.h" -#include "../curl_md5.h" -#include "../curl_sha256.h" -#include "../curl_sha512_256.h" -#include "../curlx/strparse.h" -#include "../rand.h" +#include "vauth/vauth.h" +#include "vauth/digest.h" +#include "curlx/base64.h" +#include "curl_md5.h" +#include "curl_sha256.h" +#include "curl_sha512_256.h" +#include "curlx/strparse.h" +#include "rand.h" #ifndef USE_WINDOWS_SSPI #define SESSION_ALGO 1 /* for algos with this bit set */ @@ -130,8 +130,9 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, #ifndef USE_WINDOWS_SSPI /* Convert MD5 chunk to RFC2617 (section 3.1.3) -suitable ASCII string */ -static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ - unsigned char *dest) /* 33 bytes */ +static void auth_digest_md5_to_ascii( + const unsigned char *source, /* 16 bytes */ + unsigned char *dest) /* 33 bytes */ { int i; for(i = 0; i < 16; i++) @@ -139,8 +140,9 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ } /* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ASCII string */ -static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ - unsigned char *dest) /* 65 bytes */ +static void auth_digest_sha256_to_ascii( + const unsigned char *source, /* 32 bytes */ + unsigned char *dest) /* 65 bytes */ { int i; for(i = 0; i < 32; i++) @@ -148,35 +150,26 @@ static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ } /* Perform quoted-string escaping as described in RFC2616 and its errata */ -static char *auth_digest_string_quoted(const char *source) +static char *auth_digest_string_quoted(const char *s) { - char *dest; - const char *s = source; - size_t n = 1; /* null-terminator */ - - /* Calculate size needed */ + struct dynbuf out; + curlx_dyn_init(&out, 2048); + if(!*s) /* for zero length input, make sure we return an empty string */ + return curlx_strdup(""); while(*s) { - ++n; + CURLcode result; if(*s == '"' || *s == '\\') { - ++n; + result = curlx_dyn_addn(&out, "\\", 1); + if(!result) + result = curlx_dyn_addn(&out, s, 1); } - ++s; - } - - dest = curlx_malloc(n); - if(dest) { - char *d = dest; - s = source; - while(*s) { - if(*s == '"' || *s == '\\') { - *d++ = '\\'; - } - *d++ = *s++; - } - *d = '\0'; + else + result = curlx_dyn_addn(&out, s, 1); + if(result) + return NULL; + s++; } - - return dest; + return curlx_dyn_ptr(&out); } /* Retrieves the value for a corresponding key from the challenge string @@ -348,9 +341,9 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, struct MD5_context *ctxt; char *response = NULL; unsigned char digest[MD5_DIGEST_LEN]; - char HA1_hex[2 * MD5_DIGEST_LEN + 1]; - char HA2_hex[2 * MD5_DIGEST_LEN + 1]; - char resp_hash_hex[2 * MD5_DIGEST_LEN + 1]; + char HA1_hex[(2 * MD5_DIGEST_LEN) + 1]; + char HA2_hex[(2 * MD5_DIGEST_LEN) + 1]; + char resp_hash_hex[(2 * MD5_DIGEST_LEN) + 1]; char nonce[64]; char realm[128]; char algorithm[64]; @@ -361,6 +354,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, char method[] = "AUTHENTICATE"; char qop[] = DIGEST_QOP_VALUE_STRING_AUTH; char *spn = NULL; + char *qrealm; + char *qnonce; /* Decode the challenge message */ CURLcode result = auth_decode_digest_md5_message(chlg, @@ -474,12 +469,20 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, for(i = 0; i < MD5_DIGEST_LEN; i++) curl_msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); - /* Generate the response */ - response = curl_maprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\"," - "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\"," - "response=%s,qop=%s", - userp, realm, nonce, - cnonce, nonceCount, spn, resp_hash_hex, qop); + /* escape double quotes and backslashes in the realm and nonce as + necessary */ + qrealm = auth_digest_string_quoted(realm); + qnonce = auth_digest_string_quoted(nonce); + if(qrealm && qnonce) + /* Generate the response */ + response = curl_maprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\"," + "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\"," + "response=%s,qop=%s", + userp, qrealm, qnonce, + cnonce, nonceCount, spn, resp_hash_hex, qop); + + curlx_free(qrealm); + curlx_free(qnonce); curlx_free(spn); if(!response) return CURLE_OUT_OF_MEMORY; @@ -672,16 +675,15 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Returns CURLE_OK on success. */ static CURLcode auth_create_digest_http_message( - struct Curl_easy *data, - const char *userp, - const char *passwdp, - const unsigned char *request, - const unsigned char *uripath, - struct digestdata *digest, - char **outptr, size_t *outlen, - void (*convert_to_ascii)(unsigned char *, unsigned char *), - CURLcode (*hash)(unsigned char *, const unsigned char *, - const size_t)) + struct Curl_easy *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen, + void (*convert_to_ascii)(const unsigned char *, unsigned char *), + CURLcode (*hash)(unsigned char *, const unsigned char *, const size_t)) { CURLcode result; unsigned char hashbuf[32]; /* 32 bytes/256 bits */ @@ -691,12 +693,15 @@ static CURLcode auth_create_digest_http_message( char userh[65]; char *cnonce = NULL; size_t cnonce_sz = 0; - char *userp_quoted; - char *realm_quoted; - char *nonce_quoted; - char *response = NULL; + char *userp_quoted = NULL; + char *realm_quoted = NULL; + char *nonce_quoted = NULL; char *hashthis = NULL; - char *tmp = NULL; + char *uri_quoted = NULL; + struct dynbuf response; + *outptr = NULL; + + curlx_dyn_init(&response, 4096); /* arbitrary max */ memset(hashbuf, 0, sizeof(hashbuf)); if(!digest->nc) @@ -710,27 +715,27 @@ static CURLcode auth_create_digest_http_message( #endif (unsigned char *)cnoncebuf, sizeof(cnoncebuf)); + if(!result) + result = curlx_base64_encode((uint8_t *)cnoncebuf, sizeof(cnoncebuf), + &cnonce, &cnonce_sz); if(result) - return result; - - result = curlx_base64_encode((uint8_t *)cnoncebuf, sizeof(cnoncebuf), - &cnonce, &cnonce_sz); - if(result) - return result; + goto oom; digest->cnonce = cnonce; } if(digest->userhash) { - hashthis = curl_maprintf("%s:%s", userp, - digest->realm ? digest->realm : ""); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + char *hasht = curl_maprintf("%s:%s", userp, + digest->realm ? digest->realm : ""); + if(!hasht) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } - result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis)); - curlx_free(hashthis); + result = hash(hashbuf, (unsigned char *)hasht, strlen(hasht)); + curlx_free(hasht); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, (unsigned char *)userh); } @@ -745,27 +750,31 @@ static CURLcode auth_create_digest_http_message( unq(nonce-value) ":" unq(cnonce-value) */ - hashthis = curl_maprintf("%s:%s:%s", userp, - digest->realm ? digest->realm : "", passwdp); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + hashthis = curl_maprintf("%s:%s:%s", userp, digest->realm ? + digest->realm : "", passwdp); + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis)); curlx_free(hashthis); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, ha1); if(digest->algo & SESSION_ALGO) { /* nonce and cnonce are OUTSIDE the hash */ - tmp = curl_maprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); - if(!tmp) - return CURLE_OUT_OF_MEMORY; + char *tmp = curl_maprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); + if(!tmp) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } result = hash(hashbuf, (unsigned char *)tmp, strlen(tmp)); curlx_free(tmp); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, ha1); } @@ -782,9 +791,17 @@ static CURLcode auth_create_digest_http_message( 5.1.1 of RFC 2616) */ + uri_quoted = auth_digest_string_quoted((const char *)uripath); + if(!uri_quoted) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } + hashthis = curl_maprintf("%s:%s", request, uripath); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } if(digest->qop && curl_strequal(digest->qop, "auth-int")) { /* We do not support auth-int for PUT or POST */ @@ -794,39 +811,40 @@ static CURLcode auth_create_digest_http_message( result = hash(hashbuf, (const unsigned char *)"", 0); if(result) { curlx_free(hashthis); - return result; + goto oom; } convert_to_ascii(hashbuf, (unsigned char *)hashed); hashthis2 = curl_maprintf("%s:%s", hashthis, hashed); curlx_free(hashthis); hashthis = hashthis2; + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } } - if(!hashthis) - return CURLE_OUT_OF_MEMORY; - result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis)); curlx_free(hashthis); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, ha2); - if(digest->qop) { + if(digest->qop) hashthis = curl_maprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, digest->cnonce, digest->qop, ha2); - } - else { + else hashthis = curl_maprintf("%s:%s:%s", ha1, digest->nonce, ha2); - } - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis)); curlx_free(hashthis); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, request_digest); /* For test case 64 (snooped from a Mozilla 1.3a request) @@ -843,8 +861,10 @@ static CURLcode auth_create_digest_http_message( characters. */ userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp); - if(!userp_quoted) - return CURLE_OUT_OF_MEMORY; + if(!userp_quoted) { + result = CURLE_OUT_OF_MEMORY; + goto oom; + } if(digest->realm) realm_quoted = auth_digest_string_quoted(digest->realm); else { @@ -853,98 +873,93 @@ static CURLcode auth_create_digest_http_message( realm_quoted[0] = 0; } if(!realm_quoted) { - curlx_free(userp_quoted); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto oom; } + nonce_quoted = auth_digest_string_quoted(digest->nonce); if(!nonce_quoted) { - curlx_free(realm_quoted); - curlx_free(userp_quoted); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto oom; } if(digest->qop) { - response = curl_maprintf("username=\"%s\", " - "realm=\"%s\", " - "nonce=\"%s\", " - "uri=\"%s\", " - "cnonce=\"%s\", " - "nc=%08x, " - "qop=%s, " - "response=\"%s\"", - userp_quoted, - realm_quoted, - nonce_quoted, - uripath, - digest->cnonce, - digest->nc, - digest->qop, - request_digest); + result = curlx_dyn_addf(&response, "username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "cnonce=\"%s\", " + "nc=%08x, " + "qop=%s, " + "response=\"%s\"", + userp_quoted, + realm_quoted, + nonce_quoted, + uri_quoted, + digest->cnonce, + digest->nc, + digest->qop, + request_digest); /* Increment nonce-count to use another nc value for the next request */ digest->nc++; } else { - response = curl_maprintf("username=\"%s\", " - "realm=\"%s\", " - "nonce=\"%s\", " - "uri=\"%s\", " - "response=\"%s\"", - userp_quoted, - realm_quoted, - nonce_quoted, - uripath, - request_digest); + result = curlx_dyn_addf(&response, "username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "response=\"%s\"", + userp_quoted, + realm_quoted, + nonce_quoted, + uri_quoted, + request_digest); } - curlx_free(nonce_quoted); - curlx_free(realm_quoted); - curlx_free(userp_quoted); - if(!response) - return CURLE_OUT_OF_MEMORY; + if(result) + goto oom; /* Add the optional fields */ if(digest->opaque) { - char *opaque_quoted; /* Append the opaque */ - opaque_quoted = auth_digest_string_quoted(digest->opaque); + char *opaque_quoted = auth_digest_string_quoted(digest->opaque); if(!opaque_quoted) { - curlx_free(response); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto oom; } - tmp = curl_maprintf("%s, opaque=\"%s\"", response, opaque_quoted); - curlx_free(response); + result = curlx_dyn_addf(&response, ", opaque=\"%s\"", opaque_quoted); curlx_free(opaque_quoted); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; + if(result) + goto oom; } if(digest->algorithm) { /* Append the algorithm */ - tmp = curl_maprintf("%s, algorithm=%s", response, digest->algorithm); - curlx_free(response); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; + result = curlx_dyn_addf(&response, ", algorithm=%s", digest->algorithm); + if(result) + goto oom; } if(digest->userhash) { /* Append the userhash */ - tmp = curl_maprintf("%s, userhash=true", response); - curlx_free(response); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; + result = curlx_dyn_add(&response, ", userhash=true"); + if(result) + goto oom; } /* Return the output */ - *outptr = response; - *outlen = strlen(response); + *outptr = curlx_dyn_ptr(&response); + *outlen = curlx_dyn_len(&response); + result = CURLE_OK; - return CURLE_OK; +oom: + curlx_free(nonce_quoted); + curlx_free(realm_quoted); + curlx_free(uri_quoted); + curlx_free(userp_quoted); + if(result) + curlx_dyn_free(&response); + return result; } /* diff --git a/vendor/curl/lib/vauth/digest.h b/vendor/curl/lib/vauth/digest.h index 902ffab..5b8f3ae 100644 --- a/vendor/curl/lib/vauth/digest.h +++ b/vendor/curl/lib/vauth/digest.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH diff --git a/vendor/curl/lib/vauth/digest_sspi.c b/vendor/curl/lib/vauth/digest_sspi.c index 615b4bc..f29e569 100644 --- a/vendor/curl/lib/vauth/digest_sspi.c +++ b/vendor/curl/lib/vauth/digest_sspi.c @@ -24,17 +24,17 @@ * RFC2831 DIGEST-MD5 authentication * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) -#include "vauth.h" -#include "digest.h" -#include "../curlx/multibyte.h" -#include "../curl_trc.h" -#include "../strdup.h" -#include "../strcase.h" -#include "../strerror.h" +#include "vauth/vauth.h" +#include "vauth/digest.h" +#include "curlx/multibyte.h" +#include "curl_trc.h" +#include "curlx/strdup.h" +#include "strcase.h" +#include "strerror.h" /* * Curl_auth_is_digest_supported() @@ -193,9 +193,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, status == SEC_I_COMPLETE_AND_CONTINUE) Curl_pSecFn->CompleteAuthToken(&credentials, &resp_desc); else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; -#endif + VERBOSE(char buffer[STRERROR_LEN]); Curl_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); @@ -205,10 +203,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; -#ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "schannel: InitializeSecurityContext failed: %s", Curl_sspi_strerror(status, buffer, sizeof(buffer))); -#endif return CURLE_AUTH_ERROR; } @@ -355,7 +351,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, } /* Store the challenge for use later */ - digest->input_token = (BYTE *)Curl_memdup(chlg, chlglen + 1); + digest->input_token = (BYTE *)curlx_memdup(chlg, chlglen + 1); if(!digest->input_token) return CURLE_OUT_OF_MEMORY; @@ -401,8 +397,6 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, SecBufferDesc chlg_desc; SECURITY_STATUS status; - (void)data; - /* Query the security package for DigestSSP */ status = Curl_pSecFn->QuerySecurityPackageInfo( @@ -594,9 +588,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, status == SEC_I_COMPLETE_AND_CONTINUE) Curl_pSecFn->CompleteAuthToken(&credentials, &resp_desc); else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; -#endif + VERBOSE(char buffer[STRERROR_LEN]); Curl_pSecFn->FreeCredentialsHandle(&credentials); @@ -608,10 +600,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; -#ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "schannel: InitializeSecurityContext failed: %s", Curl_sspi_strerror(status, buffer, sizeof(buffer))); -#endif return CURLE_AUTH_ERROR; } @@ -622,7 +612,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, Curl_sspi_free_identity(p_identity); } - resp = Curl_memdup0((const char *)output_token, output_token_len); + resp = curlx_memdup0((const char *)output_token, output_token_len); curlx_free(output_token); if(!resp) { return CURLE_OUT_OF_MEMORY; diff --git a/vendor/curl/lib/vauth/gsasl.c b/vendor/curl/lib/vauth/gsasl.c index b95d6e3..958f4ff 100644 --- a/vendor/curl/lib/vauth/gsasl.c +++ b/vendor/curl/lib/vauth/gsasl.c @@ -23,12 +23,12 @@ * RFC5802 SCRAM-SHA-1 authentication * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_GSASL -#include "vauth.h" -#include "../curl_trc.h" +#include "vauth/vauth.h" +#include "curl_trc.h" #include diff --git a/vendor/curl/lib/vauth/krb5_gssapi.c b/vendor/curl/lib/vauth/krb5_gssapi.c index 6a9f182..d6af18f 100644 --- a/vendor/curl/lib/vauth/krb5_gssapi.c +++ b/vendor/curl/lib/vauth/krb5_gssapi.c @@ -24,14 +24,14 @@ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5) -#include "vauth.h" -#include "../curl_sasl.h" -#include "../curl_gssapi.h" -#include "../curl_trc.h" +#include "vauth/vauth.h" +#include "curl_sasl.h" +#include "curl_gssapi.h" +#include "curl_trc.h" #if defined(__GNUC__) && defined(__APPLE__) #pragma GCC diagnostic push @@ -240,8 +240,8 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, /* Process the maximum message size the server can receive */ if(max_size > 0) { - /* The server has told us it supports a maximum receive buffer, however, as - we do not require one unless we are encrypting data, we tell the server + /* The server has told us it supports a maximum receive buffer, but as we + do not require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } diff --git a/vendor/curl/lib/vauth/krb5_sspi.c b/vendor/curl/lib/vauth/krb5_sspi.c index 8bf2091..c08b6a4 100644 --- a/vendor/curl/lib/vauth/krb5_sspi.c +++ b/vendor/curl/lib/vauth/krb5_sspi.c @@ -23,12 +23,12 @@ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5) -#include "vauth.h" -#include "../curl_trc.h" +#include "vauth/vauth.h" +#include "curl_trc.h" /* * Curl_auth_is_gssapi_supported() @@ -259,10 +259,6 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, SecPkgContext_Sizes sizes; SECURITY_STATUS status; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - /* Ensure we have a valid challenge message */ DEBUGASSERT(chlg); if(!Curl_bufref_len(chlg)) { @@ -322,8 +318,8 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, /* Process the maximum message size the server can receive */ if(max_size > 0) { - /* The server has told us it supports a maximum receive buffer, however, as - we do not require one unless we are encrypting data, we tell the server + /* The server has told us it supports a maximum receive buffer, but as we + do not require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } diff --git a/vendor/curl/lib/vauth/ntlm.c b/vendor/curl/lib/vauth/ntlm.c index 5475573..b50cfdd 100644 --- a/vendor/curl/lib/vauth/ntlm.c +++ b/vendor/curl/lib/vauth/ntlm.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI) @@ -34,12 +34,15 @@ #define DEBUG_ME 0 -#include "vauth.h" -#include "../curl_trc.h" -#include "../curl_ntlm_core.h" -#include "../rand.h" -#include "../strdup.h" -#include "../curl_endian.h" +#include "vauth/vauth.h" +#include "curl_trc.h" +#include "curl_ntlm_core.h" +#include "rand.h" +#include "curlx/strdup.h" +#include "curl_endian.h" + +/* "NTLMSSP" signature is always in ASCII regardless of the platform */ +#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" /* NTLM buffer fixed size, large enough for long user + host + domain */ #define NTLM_BUFSIZE 1024 @@ -58,6 +61,7 @@ /* Requests that the server's authentication realm be included in the Type 2 message. */ +#if DEBUG_ME /* unknown (1 << 3) */ #define NTLMFLAG_NEGOTIATE_SIGN (1 << 4) /* Specifies that authenticated communication between the client and server @@ -73,10 +77,12 @@ #define NTLMFLAG_NEGOTIATE_LM_KEY (1 << 7) /* Indicates that the LAN Manager session key should be used for signing and sealing authenticated communications. */ +#endif #define NTLMFLAG_NEGOTIATE_NTLM_KEY (1 << 9) /* Indicates that NTLM authentication is being used. */ +#if DEBUG_ME /* unknown (1 << 10) */ #define NTLMFLAG_NEGOTIATE_ANONYMOUS (1 << 11) @@ -95,11 +101,13 @@ /* Sent by the server to indicate that the server and client are on the same machine. Implies that the client may use a pre-established local security context rather than responding to the challenge. */ +#endif #define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1 << 15) /* Indicates that authenticated communication between the client and server should be signed with a "dummy" signature. */ +#if DEBUG_ME #define NTLMFLAG_TARGET_TYPE_DOMAIN (1 << 16) /* Sent by the server in the Type 2 message to indicate that the target authentication realm is a domain. */ @@ -112,11 +120,13 @@ /* Sent by the server in the Type 2 message to indicate that the target authentication realm is a share. Presumably, this is for share-level authentication. Usage is unclear. */ +#endif #define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1 << 19) /* Indicates that the NTLM2 signing and sealing scheme should be used for protecting authenticated communications. */ +#if DEBUG_ME #define NTLMFLAG_REQUEST_INIT_RESPONSE (1 << 20) /* unknown purpose */ @@ -125,11 +135,13 @@ #define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1 << 22) /* unknown purpose */ +#endif #define NTLMFLAG_NEGOTIATE_TARGET_INFO (1 << 23) /* Sent by the server in the Type 2 message to indicate that it is including a Target Information block in the message. */ +#if DEBUG_ME /* unknown (1<24) */ /* unknown (1<25) */ /* unknown (1<26) */ @@ -146,10 +158,6 @@ #define NTLMFLAG_NEGOTIATE_56 (1 << 31) /* Indicates that 56-bit encryption is supported. */ -/* "NTLMSSP" signature is always in ASCII regardless of the platform */ -#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" - -#if DEBUG_ME #define DEBUG_OUT(x) x static void ntlm_print_flags(FILE *handle, unsigned long flags) { @@ -254,10 +262,6 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data, const unsigned char *type2 = Curl_bufref_uptr(type2ref); size_t type2len = Curl_bufref_len(type2ref); -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - if(type2len >= 48) { target_info_len = Curl_read16_le(&type2[40]); target_info_offset = Curl_read32_le(&type2[44]); @@ -271,8 +275,8 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data, } curlx_free(ntlm->target_info); /* replace any previous data */ - ntlm->target_info = Curl_memdup(&type2[target_info_offset], - target_info_len); + ntlm->target_info = curlx_memdup(&type2[target_info_offset], + target_info_len); if(!ntlm->target_info) return CURLE_OUT_OF_MEMORY; } @@ -354,10 +358,6 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, const unsigned char *type2 = Curl_bufref_uptr(type2ref); size_t type2len = Curl_bufref_len(type2ref); -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - ntlm->flags = 0; if((type2len < 32) || @@ -398,7 +398,7 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) size_t i; for(i = 0; i < length; i++) { dest[2 * i] = (unsigned char)src[i]; - dest[2 * i + 1] = '\0'; + dest[(2 * i) + 1] = '\0'; } } @@ -573,7 +573,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, unsigned int ntrespoff; unsigned int ntresplen = 24; unsigned char ntresp[24]; /* fixed-size */ - unsigned char *ptr_ntresp = &ntresp[0]; + const unsigned char *ptr_ntresp = &ntresp[0]; unsigned char *ntlmv2resp = NULL; bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE); /* The fixed hostname we provide, in order to not leak our real local host diff --git a/vendor/curl/lib/vauth/ntlm_sspi.c b/vendor/curl/lib/vauth/ntlm_sspi.c index 3197dd0..12cf32b 100644 --- a/vendor/curl/lib/vauth/ntlm_sspi.c +++ b/vendor/curl/lib/vauth/ntlm_sspi.c @@ -21,14 +21,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM) -#include "vauth.h" -#include "../curl_ntlm_core.h" -#include "../curl_trc.h" -#include "../strdup.h" +#include "vauth/vauth.h" +#include "curl_ntlm_core.h" +#include "curl_trc.h" +#include "curlx/strdup.h" /* * Curl_auth_is_ntlm_supported() @@ -194,10 +194,6 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, const struct bufref *type2, struct ntlmdata *ntlm) { -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - /* Ensure we have a valid type-2 message */ if(!Curl_bufref_len(type2)) { infof(data, "NTLM handshake failure (empty type-2 message)"); @@ -205,8 +201,8 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, } /* Store the challenge for later use */ - ntlm->input_token = Curl_memdup0(Curl_bufref_ptr(type2), - Curl_bufref_len(type2)); + ntlm->input_token = curlx_memdup0(Curl_bufref_ptr(type2), + Curl_bufref_len(type2)); if(!ntlm->input_token) return CURLE_OUT_OF_MEMORY; ntlm->input_token_len = Curl_bufref_len(type2); @@ -244,9 +240,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SECURITY_STATUS status; unsigned long attrs; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif (void)passwdp; (void)userp; diff --git a/vendor/curl/lib/vauth/oauth2.c b/vendor/curl/lib/vauth/oauth2.c index a7a0613..4541d1d 100644 --- a/vendor/curl/lib/vauth/oauth2.c +++ b/vendor/curl/lib/vauth/oauth2.c @@ -23,13 +23,13 @@ * RFC6749 OAuth 2.0 Authorization Framework * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_POP3) || \ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) -#include "vauth.h" +#include "vauth/vauth.h" /* * Curl_auth_create_oauth_bearer_message() diff --git a/vendor/curl/lib/vauth/spnego_gssapi.c b/vendor/curl/lib/vauth/spnego_gssapi.c index b1ec37c..af8498b 100644 --- a/vendor/curl/lib/vauth/spnego_gssapi.c +++ b/vendor/curl/lib/vauth/spnego_gssapi.c @@ -23,14 +23,14 @@ * RFC4178 Simple and Protected GSS-API Negotiation Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(HAVE_GSSAPI) && defined(USE_SPNEGO) -#include "vauth.h" -#include "../curlx/base64.h" -#include "../curl_gssapi.h" -#include "../curl_trc.h" +#include "vauth/vauth.h" +#include "curlx/base64.h" +#include "curl_gssapi.h" +#include "curl_trc.h" #if defined(__GNUC__) && defined(__APPLE__) #pragma GCC diagnostic push diff --git a/vendor/curl/lib/vauth/spnego_sspi.c b/vendor/curl/lib/vauth/spnego_sspi.c index 4211763..1f73123 100644 --- a/vendor/curl/lib/vauth/spnego_sspi.c +++ b/vendor/curl/lib/vauth/spnego_sspi.c @@ -23,14 +23,14 @@ * RFC4178 Simple and Protected GSS-API Negotiation Mechanism * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO) -#include "vauth.h" -#include "../curlx/base64.h" -#include "../curl_trc.h" -#include "../strerror.h" +#include "vauth/vauth.h" +#include "curlx/base64.h" +#include "curl_trc.h" +#include "strerror.h" /* * Curl_auth_is_spnego_supported() @@ -95,10 +95,6 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, SecBufferDesc resp_desc; unsigned long attrs; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - if(nego->context && nego->status == SEC_E_OK) { /* We finished successfully our part of authentication, but server * rejected it (since we are again here). Exit with an error since we diff --git a/vendor/curl/lib/vauth/vauth.c b/vendor/curl/lib/vauth/vauth.c index a74c2a1..a00078f 100644 --- a/vendor/curl/lib/vauth/vauth.c +++ b/vendor/curl/lib/vauth/vauth.c @@ -21,11 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "vauth.h" -#include "../curlx/multibyte.h" -#include "../url.h" +#include "vauth/vauth.h" +#include "curlx/multibyte.h" +#include "url.h" /* * Curl_auth_build_spn() @@ -117,7 +117,7 @@ bool Curl_auth_user_contains_domain(const char *user) if(user && *user) { /* Check we have a domain name or UPN present */ - char *p = strpbrk(user, "\\/@"); + const char *p = strpbrk(user, "\\/@"); valid = (p != NULL && p > user && p < user + strlen(user) - 1); } @@ -143,11 +143,10 @@ bool Curl_auth_allowed_to_host(struct Curl_easy *data) (data->state.first_host && curl_strequal(data->state.first_host, conn->host.name) && (data->state.first_remote_port == conn->remote_port) && - (data->state.first_remote_protocol == conn->handler->protocol)); + (data->state.first_remote_protocol == conn->scheme->protocol)); } #ifdef USE_NTLM - static void ntlm_conn_dtor(void *key, size_t klen, void *entry) { struct ntlmdata *ntlm = entry; @@ -175,11 +174,9 @@ void Curl_auth_ntlm_remove(struct connectdata *conn, bool proxy) Curl_conn_meta_remove(conn, proxy ? CURL_META_NTLM_PROXY_CONN : CURL_META_NTLM_CONN); } - #endif /* USE_NTLM */ #ifdef USE_KERBEROS5 - static void krb5_conn_dtor(void *key, size_t klen, void *entry) { struct kerberos5data *krb5 = entry; @@ -201,11 +198,9 @@ struct kerberos5data *Curl_auth_krb5_get(struct connectdata *conn) } return krb5; } - #endif /* USE_KERBEROS5 */ #ifdef USE_GSASL - static void gsasl_conn_dtor(void *key, size_t klen, void *entry) { struct gsasldata *gsasl = entry; @@ -227,11 +222,9 @@ struct gsasldata *Curl_auth_gsasl_get(struct connectdata *conn) } return gsasl; } - #endif /* USE_GSASL */ #ifdef USE_SPNEGO - static void nego_conn_dtor(void *key, size_t klen, void *entry) { struct negotiatedata *nego = entry; @@ -253,5 +246,4 @@ struct negotiatedata *Curl_auth_nego_get(struct connectdata *conn, bool proxy) } return nego; } - #endif /* USE_SPNEGO */ diff --git a/vendor/curl/lib/vauth/vauth.h b/vendor/curl/lib/vauth/vauth.h index 312bd8d..3e66c89 100644 --- a/vendor/curl/lib/vauth/vauth.h +++ b/vendor/curl/lib/vauth/vauth.h @@ -23,11 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "../bufref.h" -#include "../curlx/dynbuf.h" -#include "../urldata.h" +#include "bufref.h" +#include "curlx/dynbuf.h" +#include "urldata.h" struct Curl_easy; struct connectdata; @@ -49,7 +49,7 @@ struct gsasldata; #endif #ifdef USE_WINDOWS_SSPI -#include "../curl_sspi.h" +#include "curl_sspi.h" #define GSS_ERROR(status) ((status) & 0x80000000) #endif @@ -110,7 +110,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, const char *userp, const char *passwdp, const unsigned char *request, - const unsigned char *uri, + const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen); @@ -150,7 +150,7 @@ CURLcode Curl_auth_gsasl_token(struct Curl_easy *data, struct bufref *out); /* This is used to clean up the gsasl specific data */ -void Curl_auth_gsasl_cleanup(struct gsasldata *digest); +void Curl_auth_gsasl_cleanup(struct gsasldata *gsasl); #endif #ifdef USE_NTLM @@ -199,13 +199,13 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, const char *userp, const char *passwdp, const char *service, - const char *host, + const char *hostname, struct ntlmdata *ntlm, struct bufref *out); /* This is used to decode a base64 encoded NTLM type-2 message */ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, - const struct bufref *type2, + const struct bufref *type2ref, struct ntlmdata *ntlm); /* This is used to generate a base64 encoded NTLM type-3 message */ diff --git a/vendor/curl/lib/version.c b/vendor/curl/lib/version.c index 697d9ea..7ccd875 100644 --- a/vendor/curl/lib/version.c +++ b/vendor/curl/lib/version.c @@ -341,6 +341,9 @@ static const char * const supported_protocols[] = { #ifndef CURL_DISABLE_MQTT "mqtt", #endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_MQTT) + "mqtts", +#endif #ifndef CURL_DISABLE_POP3 "pop3", #endif @@ -513,9 +516,13 @@ static const struct feat features_table[] = { #ifdef USE_LIBPSL FEATURE("PSL", NULL, CURL_VERSION_PSL), #endif +#ifdef USE_SSL #ifdef USE_APPLE_SECTRUST FEATURE("AppleSecTrust", NULL, 0), +#elif defined(CURL_CA_NATIVE) + FEATURE("NativeCA", NULL, 0), #endif +#endif /* USE_SSL */ #ifdef USE_SPNEGO FEATURE("SPNEGO", NULL, CURL_VERSION_SPNEGO), #endif @@ -534,9 +541,6 @@ static const struct feat features_table[] = { #ifdef USE_TLS_SRP FEATURE("TLS-SRP", NULL, CURL_VERSION_TLSAUTH_SRP), #endif -#ifdef CURLDEBUG - FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG), -#endif #if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE) FEATURE("Unicode", NULL, CURL_VERSION_UNICODE), #endif @@ -648,7 +652,7 @@ curl_version_info_data *curl_version_info(CURLversion stamp) #endif #ifdef HAVE_ZSTD - version_info.zstd_ver_num = (unsigned int)ZSTD_versionNumber(); + version_info.zstd_ver_num = ZSTD_versionNumber(); zstd_version(zstd_buffer, sizeof(zstd_buffer)); version_info.zstd_version = zstd_buffer; #endif @@ -683,6 +687,10 @@ curl_version_info_data *curl_version_info(CURLversion stamp) feature_names[n++] = p->name; } +#ifdef DEBUGBUILD + features |= CURL_VERSION_CURLDEBUG; /* for compatibility */ +#endif + feature_names[n] = NULL; /* Terminate array. */ version_info.features = features; diff --git a/vendor/curl/lib/vquic/curl_ngtcp2.c b/vendor/curl/lib/vquic/curl_ngtcp2.c index 97ef98b..48905d3 100644 --- a/vendor/curl/lib/vquic/curl_ngtcp2.c +++ b/vendor/curl/lib/vquic/curl_ngtcp2.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) #include @@ -36,37 +36,38 @@ #else #include #endif -#include "../vtls/openssl.h" +#include "vtls/openssl.h" #elif defined(USE_GNUTLS) #include -#include "../vtls/gtls.h" +#include "vtls/gtls.h" #elif defined(USE_WOLFSSL) #include -#include "../vtls/wolfssl.h" +#include "vtls/wolfssl.h" #endif -#include "../urldata.h" -#include "../url.h" -#include "../uint-hash.h" -#include "../curl_trc.h" -#include "../rand.h" -#include "../multiif.h" -#include "../cfilters.h" -#include "../cf-socket.h" -#include "../connect.h" -#include "../progress.h" -#include "../curlx/dynbuf.h" -#include "../http1.h" -#include "../select.h" -#include "../transfer.h" -#include "../bufref.h" -#include "vquic.h" -#include "vquic_int.h" -#include "vquic-tls.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" -#include "../vtls/vtls_scache.h" -#include "curl_ngtcp2.h" +#include "urldata.h" +#include "url.h" +#include "uint-hash.h" +#include "curl_trc.h" +#include "rand.h" +#include "multiif.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "curlx/fopen.h" +#include "curlx/dynbuf.h" +#include "http1.h" +#include "select.h" +#include "transfer.h" +#include "bufref.h" +#include "vquic/vquic.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" +#include "vtls/vtls_scache.h" +#include "vquic/curl_ngtcp2.h" #define QUIC_MAX_STREAMS (256 * 1024) @@ -231,7 +232,8 @@ struct h3_stream_ctx { size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ - uint64_t download_unacked; /* bytes not acknowledged yet */ + uint64_t rx_offset; /* current receive offset */ + uint64_t rx_offset_max; /* allowed receive offset */ uint64_t window_size_max; /* max flow control window set for stream */ int status_code; /* HTTP status code */ CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ @@ -273,6 +275,9 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; stream->id = -1; + stream->rx_offset = 0; + stream->rx_offset_max = H3_STREAM_WINDOW_SIZE_INITIAL; + /* on send, we control how much we put into the buffer */ Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); @@ -371,10 +376,6 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) } } -/* ngtcp2 default congestion controller does not perform pacing. Limit - the maximum packet burst to MAX_PKT_BURST packets. */ -#define MAX_PKT_BURST 10 - struct pkt_io_ctx { struct Curl_cfilter *cf; struct Curl_easy *data; @@ -390,8 +391,8 @@ static void pktx_update_time(struct Curl_easy *data, const struct curltime *pnow = Curl_pgrs_now(data); vquic_ctx_update_time(&ctx->q, pnow); - pktx->ts = (ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS + - (ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS; + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); } static void pktx_init(struct pkt_io_ctx *pktx, @@ -405,8 +406,8 @@ static void pktx_init(struct pkt_io_ctx *pktx, pktx->data = data; ngtcp2_path_storage_zero(&pktx->ps); vquic_ctx_set_time(&ctx->q, pnow); - pktx->ts = (ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS + - (ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS; + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); } static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, @@ -445,7 +446,7 @@ static void qlog_callback(void *user_data, uint32_t flags, ssize_t rc = write(ctx->qlogfd, data, datalen); if(rc == -1) { /* on write error, stop further write attempts */ - close(ctx->qlogfd); + curlx_close(ctx->qlogfd); ctx->qlogfd = -1; } } @@ -511,7 +512,7 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { const ngtcp2_transport_params *rp; rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); @@ -610,21 +611,15 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; - nghttp3_ssize nconsumed; + nghttp3_ssize rc; + uint64_t nconsumed; int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)offset; - (void)data; - nconsumed = - nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); - if(!data) - data = CF_DATA_CURRENT(cf); - if(data) - CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", - stream_id, buflen, nconsumed); - if(nconsumed < 0) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + rc = nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); + if(rc < 0) { if(data && stream) { CURL_TRC_CF(data, cf, "[%" PRId64 "] error on known stream, " "reset=%d, closed=%d", @@ -632,13 +627,18 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, } return NGTCP2_ERR_CALLBACK_FAILURE; } - - /* number of bytes inside buflen which consists of framing overhead - * including QPACK HEADERS. In other words, it does not consume payload of - * DATA frame. */ - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, (uint64_t)nconsumed); - ngtcp2_conn_extend_max_offset(tconn, (uint64_t)nconsumed); - + nconsumed = (uint64_t)rc; + if(nconsumed) { + /* number of bytes inside buflen which consists of framing overhead + * including QPACK HEADERS. In other words, it does not consume payload of + * DATA frame. */ + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); + ngtcp2_conn_extend_max_offset(tconn, nconsumed); + if(stream) { + stream->rx_offset += nconsumed; + stream->rx_offset_max += nconsumed; + } + } return 0; } @@ -705,7 +705,6 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, (void)tconn; (void)final_size; (void)app_error_code; - (void)data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); @@ -785,7 +784,7 @@ static void cb_rand(uint8_t *dest, size_t destlen, result = Curl_rand(NULL, dest, destlen); if(result) { /* cb_rand is only used for non-cryptographic context. If Curl_rand - failed, just fill 0 and call it *random*. */ + failed, fill 0 and call it *random*. */ memset(dest, 0, destlen); } } @@ -1046,57 +1045,50 @@ static void h3_xfer_write_resp(struct Curl_cfilter *cf, } } -static void cf_ngtcp2_stream_update_window(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream) -{ - /* stream receive max window size for flow control. We can only - * grow it from the initial window size */ - uint64_t swin_max = data->progress.dl.rlimit.rate_per_step ? - data->progress.dl.rlimit.rate_per_step : H3_STREAM_WINDOW_SIZE_MAX; - if(swin_max > stream->window_size_max) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rc = ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, - swin_max - stream->window_size_max); - if(rc) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] extend_max_stream_offset to %" - PRIu64 " -> %s (%d)", - stream->id, swin_max, ngtcp2_strerror(rc), rc); - DEBUGASSERT(0); - } - stream->window_size_max = swin_max; - } -} - -static void cf_ngtcp2_ack_stream(struct Curl_cfilter *cf, +static void cf_ngtcp2_upd_rx_win(struct Curl_cfilter *cf, struct Curl_easy *data, struct h3_stream_ctx *stream) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - curl_off_t avail; - uint64_t ack_len = 0; - - /* How many byte to ack on the stream? */ + uint64_t cur_win, wanted_win = H3_STREAM_WINDOW_SIZE_MAX; /* how much does rate limiting allow us to acknowledge? */ - avail = Curl_rlimit_avail(&data->progress.dl.rlimit, - Curl_pgrs_now(data)); - if(avail == CURL_OFF_T_MAX) { /* no rate limit, ack all */ - ack_len = stream->download_unacked; - } - else if(avail > 0) { - ack_len = CURLMIN(stream->download_unacked, (uint64_t)avail); + if(Curl_rlimit_active(&data->progress.dl.rlimit)) { + int64_t avail; + + /* start rate limit updates only after first bytes arrived */ + if(!stream->rx_offset) + return; + + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, + Curl_pgrs_now(data)); + if(avail <= 0) { + /* nothing available, do not extend the rx offset */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 + " tokens)", stream->id, avail); + return; + } + wanted_win = CURLMIN((uint64_t)avail, H3_STREAM_WINDOW_SIZE_MAX); } - if(ack_len) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %" PRIu64 - "/%" PRIu64 " bytes of DATA", stream->id, - ack_len, stream->download_unacked); - ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, ack_len); - stream->download_unacked -= ack_len; + if(stream->rx_offset_max < stream->rx_offset) { + DEBUGASSERT(0); + return; } + cur_win = stream->rx_offset_max - stream->rx_offset; + + if(wanted_win > cur_win) { + uint64_t delta = wanted_win - cur_win; - cf_ngtcp2_stream_update_window(cf, data, stream); + if(UINT64_MAX - delta < stream->rx_offset_max) + delta = UINT64_MAX - stream->rx_offset_max; + if(delta) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] rx window, extend by %" PRIu64 + " bytes", stream->id, delta); + stream->rx_offset_max += delta; + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, delta); + } + } } static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, @@ -1115,15 +1107,15 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, return NGHTTP3_ERR_CALLBACK_FAILURE; h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE); - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, blen); ngtcp2_conn_extend_max_offset(ctx->qconn, blen); - if(UINT64_MAX - blen < stream->download_unacked) - stream->download_unacked = UINT64_MAX; /* unlikely */ - else - stream->download_unacked += blen; + stream->rx_offset += blen; + if(stream->rx_offset_max < stream->rx_offset) + stream->rx_offset_max = stream->rx_offset; - cf_ngtcp2_ack_stream(cf, data, stream); + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRId64, + stream->id, blen, stream->rx_offset_max - stream->rx_offset); + cf_ngtcp2_upd_rx_win(cf, data, stream); return 0; } @@ -1133,13 +1125,18 @@ static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)conn; - (void)stream_user_data; /* nghttp3 has consumed bytes on the QUIC stream and we need to * tell the QUIC connection to increase its flow control */ ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + if(stream) { + stream->rx_offset += consumed; + stream->rx_offset_max += consumed; + } return 0; } @@ -1158,7 +1155,8 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, if(!stream) return 0; /* add a CRLF only if we have received some headers */ - h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); + h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), + (bool)stream->closed); CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", stream_id, stream->status_code); @@ -1263,7 +1261,6 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, struct Curl_easy *data = stream_user_data; int rv; (void)conn; - (void)data; rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, app_error_code); @@ -1372,7 +1369,23 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, (void)cf; *pnread = 0; if(stream->reset) { - failf(data, "HTTP/3 stream %" PRId64 " reset by server", stream->id); + if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { + infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " + "on a new connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + data->state.refused_stream = TRUE; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ + } + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " + "but we did not want a body anyway, ignore error 0x%" + PRIx64 " %s", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + return CURLE_OK; + } + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; } else if(!stream->resp_hds_complete) { @@ -1394,9 +1407,11 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_call_data save; struct pkt_io_ctx pktx; CURLcode result = CURLE_OK; + int i; (void)ctx; (void)buf; + NOVERBOSE((void)blen); CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); @@ -1418,23 +1433,31 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } - cf_ngtcp2_ack_stream(cf, data, stream); + cf_ngtcp2_upd_rx_win(cf, data, stream); - if(cf_progress_ingress(cf, data, &pktx)) { - result = CURLE_RECV_ERROR; - goto out; - } + /* first check for results/closed already known without touching + * the connection. For an already failed/closed stream, errors on + * the connection do not count. + * Then handle incoming data and check for failed/closed again. + */ + for(i = 0; i < 2; ++i) { + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); + cf_ngtcp2_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + result = recv_closed_stream(cf, data, stream, pnread); + goto out; + } - if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); - result = stream->xfer_result; - goto out; - } - else if(stream->closed) { - result = recv_closed_stream(cf, data, stream, pnread); - goto out; + if(!i && cf_progress_ingress(cf, data, &pktx)) { + result = CURLE_RECV_ERROR; + goto out; + } } + result = CURLE_AGAIN; out: @@ -1580,7 +1603,6 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, 0, pnwritten); if(result) goto out; - if(!stream->h1.done) { /* need more data */ goto out; @@ -1661,7 +1683,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, goto out; } - cf_ngtcp2_stream_update_window(cf, data, stream); + cf_ngtcp2_upd_rx_win(cf, data, stream); if(Curl_trc_is_verbose(data)) { infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", @@ -1718,7 +1740,7 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); goto out; } - stream = H3_STREAM_CTX(ctx, data); + VERBOSE(stream = H3_STREAM_CTX(ctx, data)); } else if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); @@ -1802,7 +1824,7 @@ static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", buflen, gso_size, ecn); ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, - (socklen_t)ctx->q.local_addrlen); + ctx->q.local_addrlen); ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, remote_addrlen); pi.ecn = (uint8_t)ecn; @@ -2026,8 +2048,8 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, } else if(nread > gsolen || (gsolen > path_max_payload_size && nread != gsolen)) { - /* The just added packet is a PMTUD *or* the one(s) before the - * just added were PMTUD and the last one is smaller. + /* The added packet is a PMTUD *or* the one(s) before the + * added were PMTUD and the last one is smaller. * Flush the buffer before the last add. */ curlcode = vquic_send_tail_split(cf, data, &ctx->q, gsolen, nread, nread); @@ -2041,8 +2063,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, pktcnt = 0; } else if(nread < gsolen) { - /* Reached MAX_PKT_BURST *or* - * the capacity of our buffer *or* + /* Reached capacity of our buffer *or* * last add was shorter than the previous ones, flush */ break; } @@ -2129,7 +2150,7 @@ static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) if(!ctx->initialized) return; if(ctx->qlogfd != -1) { - close(ctx->qlogfd); + curlx_close(ctx->qlogfd); } ctx->qlogfd = -1; Curl_vquic_tls_cleanup(&ctx->tls); @@ -2319,6 +2340,7 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) #ifdef USE_GNUTLS +#ifdef CURLVERBOSE static const char *gtls_hs_msg_name(int mtype) { switch(mtype) { @@ -2345,6 +2367,7 @@ static const char *gtls_hs_msg_name(int mtype) } return "Unknown"; } +#endif static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, unsigned when, unsigned int incoming, @@ -2508,7 +2531,7 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, #endif #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) - if((!ctx->earlydata_max)) { + if(!ctx->earlydata_max) { CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); } else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { @@ -2628,7 +2651,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, #elif defined(USE_WOLFSSL) ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl); #else - #error "ngtcp2 TLS backend not defined" +#error "ngtcp2 TLS backend not defined" #endif ngtcp2_ccerr_default(&ctx->last_error); @@ -2710,7 +2733,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, default: if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", - (unsigned int)(cerr->error_code & 0xffu)); + (unsigned int)(cerr->error_code & 0xffU)); } else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { CURL_TRC_CF(data, cf, "connection refused by server"); @@ -2723,7 +2746,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, } } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(result) { struct ip_quadruple ip; @@ -2896,7 +2919,6 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_cfilter *cf = NULL; CURLcode result; - (void)data; ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; diff --git a/vendor/curl/lib/vquic/curl_ngtcp2.h b/vendor/curl/lib/vquic/curl_ngtcp2.h index 1e9868e..1a87faf 100644 --- a/vendor/curl/lib/vquic/curl_ngtcp2.h +++ b/vendor/curl/lib/vquic/curl_ngtcp2.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) @@ -46,7 +46,7 @@ struct Curl_cfilter; -#include "../urldata.h" +#include "urldata.h" void Curl_ngtcp2_ver(char *p, size_t len); diff --git a/vendor/curl/lib/vquic/curl_osslq.c b/vendor/curl/lib/vquic/curl_osslq.c deleted file mode 100644 index 4648797..0000000 --- a/vendor/curl/lib/vquic/curl_osslq.c +++ /dev/null @@ -1,2458 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "../curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_OPENSSL_QUIC) && \ - defined(USE_NGHTTP3) - -#include -#include -#include -#include - -#include "../urldata.h" -#include "../curl_trc.h" -#include "../multiif.h" -#include "../cfilters.h" -#include "../cf-socket.h" -#include "../connect.h" -#include "../progress.h" -#include "../curlx/dynbuf.h" -#include "../http1.h" -#include "../select.h" -#include "../uint-hash.h" -#include "vquic.h" -#include "vquic_int.h" -#include "vquic-tls.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" -#include "../vtls/openssl.h" -#include "curl_osslq.h" -#include "../url.h" -#include "../bufref.h" -#include "../curlx/strerr.h" -#include "../curlx/strcopy.h" - -/* A stream window is the maximum amount we need to buffer for - * each active transfer. We use HTTP/3 flow control and only ACK - * when we take things out of the buffer. - * Chunk size is large enough to take a full DATA frame */ -#define H3_STREAM_WINDOW_SIZE (128 * 1024) -#define H3_STREAM_CHUNK_SIZE (16 * 1024) -/* The pool keeps spares around and half of a full stream window - * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffer to not keep - * spares. Memory consumption goes down when streams run empty, - * have a large upload done, etc. */ -#define H3_STREAM_POOL_SPARES \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) / 2 -/* Receive and Send max number of chunks just follows from the - * chunk size and window size */ -#define H3_STREAM_RECV_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) -#define H3_STREAM_SEND_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) - -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) -typedef uint32_t sslerr_t; -#else -typedef unsigned long sslerr_t; -#endif - - -/* How to access `call_data` from a cf_osslq filter */ -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) ((struct cf_osslq_ctx *)(cf)->ctx)->call_data - -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static const char *osslq_SSL_ERROR_to_str(int err) -{ - switch(err) { - case SSL_ERROR_NONE: - return "SSL_ERROR_NONE"; - case SSL_ERROR_SSL: - return "SSL_ERROR_SSL"; - case SSL_ERROR_WANT_READ: - return "SSL_ERROR_WANT_READ"; - case SSL_ERROR_WANT_WRITE: - return "SSL_ERROR_WANT_WRITE"; - case SSL_ERROR_WANT_X509_LOOKUP: - return "SSL_ERROR_WANT_X509_LOOKUP"; - case SSL_ERROR_SYSCALL: - return "SSL_ERROR_SYSCALL"; - case SSL_ERROR_ZERO_RETURN: - return "SSL_ERROR_ZERO_RETURN"; - case SSL_ERROR_WANT_CONNECT: - return "SSL_ERROR_WANT_CONNECT"; - case SSL_ERROR_WANT_ACCEPT: - return "SSL_ERROR_WANT_ACCEPT"; -#ifdef SSL_ERROR_WANT_ASYNC /* OpenSSL 1.1.0+, LibreSSL 3.6.0+ */ - case SSL_ERROR_WANT_ASYNC: - return "SSL_ERROR_WANT_ASYNC"; -#endif -#ifdef SSL_ERROR_WANT_ASYNC_JOB /* OpenSSL 1.1.0+, LibreSSL 3.6.0+ */ - case SSL_ERROR_WANT_ASYNC_JOB: - return "SSL_ERROR_WANT_ASYNC_JOB"; -#endif -#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB /* OpenSSL 1.1.1, LibreSSL 3.6.0+ */ - case SSL_ERROR_WANT_CLIENT_HELLO_CB: - return "SSL_ERROR_WANT_CLIENT_HELLO_CB"; -#endif - default: - return "SSL_ERROR unknown"; - } -} - -/* Return error string for last OpenSSL error */ -static char *osslq_strerror(unsigned long error, char *buf, size_t size) -{ - DEBUGASSERT(size); - *buf = '\0'; - -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) - ERR_error_string_n((uint32_t)error, buf, size); -#else - ERR_error_string_n(error, buf, size); -#endif - - if(!*buf) { - const char *msg = error ? "Unknown error" : "No error"; - if(strlen(msg) < size) - curlx_strcopy(buf, size, msg, strlen(msg)); - } - - return buf; -} - -static CURLcode make_bio_addr(BIO_ADDR **pbio_addr, - const struct Curl_sockaddr_ex *addr) -{ - BIO_ADDR *bio_addr; - CURLcode result = CURLE_FAILED_INIT; - - bio_addr = BIO_ADDR_new(); - if(!bio_addr) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - switch(addr->family) { - case AF_INET: { - struct sockaddr_in * const sin = - (struct sockaddr_in * const)CURL_UNCONST(&addr->curl_sa_addr); - if(!BIO_ADDR_rawmake(bio_addr, AF_INET, &sin->sin_addr, - sizeof(sin->sin_addr), sin->sin_port)) { - goto out; - } - result = CURLE_OK; - break; - } -#ifdef USE_IPV6 - case AF_INET6: { - struct sockaddr_in6 * const sin = - (struct sockaddr_in6 * const)CURL_UNCONST(&addr->curl_sa_addr); - if(!BIO_ADDR_rawmake(bio_addr, AF_INET6, &sin->sin6_addr, - sizeof(sin->sin6_addr), sin->sin6_port)) { - goto out; - } - result = CURLE_OK; - break; - } -#endif /* USE_IPV6 */ - default: - /* sunsupported */ - DEBUGASSERT(0); - break; - } - -out: - if(result && bio_addr) { - BIO_ADDR_free(bio_addr); - bio_addr = NULL; - } - *pbio_addr = bio_addr; - return result; -} - -/* QUIC stream (not necessarily H3) */ -struct cf_osslq_stream { - int64_t id; - SSL *ssl; - struct bufq recvbuf; /* QUIC war data recv buffer */ - BIT(recvd_eos); - BIT(closed); - BIT(reset); - BIT(send_blocked); -}; - -static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s, - SSL *conn, - uint64_t flags, - struct bufc_pool *bufcp, - void *user_data) -{ - DEBUGASSERT(!s->ssl); - Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE); - s->ssl = SSL_new_stream(conn, flags); - if(!s->ssl) { - return CURLE_FAILED_INIT; - } - s->id = SSL_get_stream_id(s->ssl); - SSL_set_app_data(s->ssl, user_data); - return CURLE_OK; -} - -static void cf_osslq_stream_cleanup(struct cf_osslq_stream *s) -{ - if(s->ssl) { - SSL_set_app_data(s->ssl, NULL); - SSL_free(s->ssl); - } - Curl_bufq_free(&s->recvbuf); - memset(s, 0, sizeof(*s)); -} - -static void cf_osslq_stream_close(struct cf_osslq_stream *s) -{ - if(s->ssl) { - SSL_free(s->ssl); - s->ssl = NULL; - } -} - -struct cf_osslq_h3conn { - nghttp3_conn *conn; - nghttp3_settings settings; - struct cf_osslq_stream s_ctrl; - struct cf_osslq_stream s_qpack_enc; - struct cf_osslq_stream s_qpack_dec; - struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */ - size_t remote_ctrl_n; /* number of peer streams opened */ -}; - -static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3) -{ - size_t i; - - if(h3->conn) - nghttp3_conn_del(h3->conn); - cf_osslq_stream_cleanup(&h3->s_ctrl); - cf_osslq_stream_cleanup(&h3->s_qpack_enc); - cf_osslq_stream_cleanup(&h3->s_qpack_dec); - for(i = 0; i < h3->remote_ctrl_n; ++i) { - cf_osslq_stream_cleanup(&h3->remote_ctrl[i]); - } -} - -struct cf_osslq_ctx { - struct cf_quic_ctx q; - struct ssl_peer peer; - struct curl_tls_ctx tls; - struct cf_call_data call_data; - struct cf_osslq_h3conn h3; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct curltime first_byte_at; /* when first byte was recvd */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct uint_hash streams; /* hash `data->mid` to `h3_stream_ctx` */ - size_t max_stream_window; /* max flow window for one stream */ - SSL_POLL_ITEM *poll_items; /* Array for polling on writable state */ - struct Curl_easy **curl_items; /* Array of easy objs */ - size_t items_max; /* max elements in poll/curl_items */ - BIT(initialized); - BIT(got_first_byte); /* if first byte was received */ - BIT(x509_store_setup); /* if x509 store has been set up */ - BIT(protocol_shutdown); /* QUIC connection is shut down */ - BIT(need_recv); /* QUIC connection needs to receive */ - BIT(need_send); /* QUIC connection needs to send */ -}; - -static void h3_stream_hash_free(unsigned int id, void *stream); - -static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx) -{ - DEBUGASSERT(!ctx->initialized); - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); - ctx->poll_items = NULL; - ctx->curl_items = NULL; - ctx->items_max = 0; - ctx->initialized = TRUE; -} - -static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx) -{ - if(ctx && ctx->initialized) { - Curl_bufcp_free(&ctx->stream_bufcp); - Curl_uint32_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - curlx_free(ctx->poll_items); - curlx_free(ctx->curl_items); - } - curlx_free(ctx); -} - -static void cf_osslq_ctx_close(struct cf_osslq_ctx *ctx) -{ - struct cf_call_data save = ctx->call_data; - - cf_osslq_h3conn_cleanup(&ctx->h3); - Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); - ctx->call_data = save; -} - -static CURLcode cf_osslq_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct cf_call_data save; - CURLcode result = CURLE_OK; - int rc; - - CF_DATA_SAVE(save, cf, data); - - if(cf->shutdown || ctx->protocol_shutdown) { - *done = TRUE; - return CURLE_OK; - } - - CF_DATA_SAVE(save, cf, data); - *done = FALSE; - ctx->need_send = FALSE; - ctx->need_recv = FALSE; - - rc = SSL_shutdown_ex(ctx->tls.ossl.ssl, - SSL_SHUTDOWN_FLAG_NO_BLOCK, NULL, 0); - if(rc == 0) { /* ongoing */ - CURL_TRC_CF(data, cf, "shutdown ongoing"); - ctx->need_recv = TRUE; - goto out; - } - else if(rc == 1) { /* done */ - CURL_TRC_CF(data, cf, "shutdown finished"); - *done = TRUE; - goto out; - } - else { - long sslerr; - char err_buffer[256]; - int err = SSL_get_error(ctx->tls.ossl.ssl, rc); - - switch(err) { - case SSL_ERROR_NONE: - case SSL_ERROR_ZERO_RETURN: - CURL_TRC_CF(data, cf, "shutdown not received, but closed"); - *done = TRUE; - goto out; - case SSL_ERROR_WANT_READ: - /* SSL has send its notify and now wants to read the reply - * from the server. We are not really interested in that. */ - CURL_TRC_CF(data, cf, "shutdown sent, want receive"); - ctx->need_recv = TRUE; - goto out; - case SSL_ERROR_WANT_WRITE: - CURL_TRC_CF(data, cf, "shutdown send blocked"); - ctx->need_send = TRUE; - goto out; - default: - /* We give up on this. */ - sslerr = ERR_get_error(); - CURL_TRC_CF(data, cf, "shutdown, ignore recv error: '%s', errno %d", - (sslerr ? - osslq_strerror(sslerr, err_buffer, sizeof(err_buffer)) : - osslq_SSL_ERROR_to_str(err)), - SOCKERRNO); - *done = TRUE; - result = CURLE_OK; - goto out; - } - } -out: - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->tls.ossl.ssl) { - CURL_TRC_CF(data, cf, "cf_osslq_close()"); - if(!cf->shutdown && !ctx->protocol_shutdown) { - /* last best effort, which OpenSSL calls a "rapid" shutdown. */ - SSL_shutdown_ex(ctx->tls.ossl.ssl, - (SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID), - NULL, 0); - } - cf_osslq_ctx_close(ctx); - } - - cf->connected = FALSE; - CF_DATA_RESTORE(cf, save); -} - -static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - CURL_TRC_CF(data, cf, "destroy"); - if(ctx) { - CURL_TRC_CF(data, cf, "cf_osslq_destroy()"); - if(ctx->tls.ossl.ssl) - cf_osslq_ctx_close(ctx); - cf_osslq_ctx_free(ctx); - } - cf->ctx = NULL; - /* No CF_DATA_RESTORE(cf, save) possible */ - (void)save; -} - -static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, - SSL *stream_ssl, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - int64_t stream_id = (int64_t)SSL_get_stream_id(stream_ssl); - int stype = SSL_get_stream_type(stream_ssl); - /* This could be a GREASE stream, e.g. HTTP/3 rfc9114 ch 6.2.3 - * reserved stream type that is supposed to be discarded silently. - * BUT OpenSSL does not offer this information to us. So, we silently - * ignore all such streams we do not expect. */ - switch(stype) { - case SSL_STREAM_TYPE_READ: { - struct cf_osslq_stream *nstream; - if(h3->remote_ctrl_n >= CURL_ARRAYSIZE(h3->remote_ctrl)) { - /* rejected, we are full */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] reject remote uni stream", - stream_id); - SSL_free(stream_ssl); - return CURLE_OK; - } - nstream = &h3->remote_ctrl[h3->remote_ctrl_n++]; - nstream->id = stream_id; - nstream->ssl = stream_ssl; - Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE); - CURL_TRC_CF(data, cf, "[%" PRId64 "] accepted remote uni stream", - stream_id); - return CURLE_OK; - } - default: - CURL_TRC_CF(data, cf, "[%" PRId64 "] reject remote %s" - " stream, type=%x", stream_id, - (stype == SSL_STREAM_TYPE_BIDI) ? "bidi" : "write", stype); - SSL_free(stream_ssl); - return CURLE_OK; - } -} - -static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, - struct Curl_easy *data, - int detail, CURLcode def_result) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = def_result; - sslerr_t errdetail; - char ebuf[256] = "unknown"; - const char *err_descr = ebuf; - long lerr; - int lib; - int reason; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - - errdetail = ERR_get_error(); - lib = ERR_GET_LIB(errdetail); - reason = ERR_GET_REASON(errdetail); - - if((lib == ERR_LIB_SSL) && - ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || - (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { - result = CURLE_PEER_FAILED_VERIFICATION; - - lerr = SSL_get_verify_result(ctx->tls.ossl.ssl); - if(lerr != X509_V_OK) { - ssl_config->certverifyresult = lerr; - curl_msnprintf(ebuf, sizeof(ebuf), "SSL certificate problem: %s", - X509_verify_cert_error_string(lerr)); - } - else - err_descr = "SSL certificate verification failed"; - } -#ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED - /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on - OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */ - else if((lib == ERR_LIB_SSL) && - (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { - /* If client certificate is required, communicate the - error to client */ - result = CURLE_SSL_CLIENTCERT; - osslq_strerror(errdetail, ebuf, sizeof(ebuf)); - } -#endif - else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) { - ctx->protocol_shutdown = TRUE; - err_descr = "QUIC connection has been shut down"; - result = def_result; - } - else { - result = def_result; - osslq_strerror(errdetail, ebuf, sizeof(ebuf)); - } - - /* detail is already set to the SSL error above */ - - /* If we e.g. use SSLv2 request-method and the server does not like us - * (RST connection, etc.), OpenSSL gives no explanation whatsoever and - * the SO_ERROR is also lost. - */ - if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { - char extramsg[80] = ""; - int sockerr = SOCKERRNO; - struct ip_quadruple ip; - - if(sockerr && detail == SSL_ERROR_SYSCALL) - curlx_strerror(sockerr, extramsg, sizeof(extramsg)); - if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) - failf(data, "QUIC connect: %s in connection to %s:%d (%s)", - extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail), - ctx->peer.dispname, ip.remote_port, ip.remote_ip); - } - else { - /* Could be a CERT problem */ - failf(data, "%s", err_descr); - } - return result; -} - -static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); -} - -/** - * All about the H3 internals of a stream - */ -struct h3_stream_ctx { - struct cf_osslq_stream s; - struct bufq sendbuf; /* h3 request body */ - struct bufq recvbuf; /* h3 response body */ - struct h1_req_parser h1; /* h1 request parsing */ - size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - size_t recv_buf_nonflow; /* buffered bytes, - not counting for flow control */ - uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ - curl_off_t download_recvd; /* number of response DATA bytes received */ - int status_code; /* HTTP status code */ - BIT(resp_hds_complete); /* we have a complete, final response */ - BIT(closed); /* TRUE on stream close */ - BIT(reset); /* TRUE on stream reset */ - BIT(send_closed); /* stream is local closed */ - BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ -}; - -static void h3_stream_ctx_free(struct h3_stream_ctx *stream) -{ - cf_osslq_stream_cleanup(&stream->s); - Curl_bufq_free(&stream->sendbuf); - Curl_bufq_free(&stream->recvbuf); - Curl_h1_req_parse_free(&stream->h1); - curlx_free(stream); -} - -static void h3_stream_hash_free(unsigned int id, void *stream) -{ - (void)id; - DEBUGASSERT(stream); - h3_stream_ctx_free((struct h3_stream_ctx *)stream); -} - -static CURLcode h3_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - if(!data) - return CURLE_FAILED_INIT; - - if(stream) - return CURLE_OK; - - stream = curlx_calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - stream->s.id = -1; - /* on send, we control how much we put into the buffer */ - Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, - H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - stream->sendbuf_len_in_flight = 0; - /* on recv, we need a flexible buffer limit since we also write - * headers to it that are not counted against the nghttp3 flow limits. */ - Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, - H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - stream->recv_buf_nonflow = 0; - Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - - if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { - h3_stream_ctx_free(stream); - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - (void)cf; - if(stream) { - CURL_TRC_CF(data, cf, "[%" PRIu64 "] easy handle is done", stream->s.id); - if(ctx->h3.conn && (stream->s.id >= 0) && !stream->closed) { - nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id); - nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id, - NGHTTP3_H3_REQUEST_CANCELLED); - nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL); - stream->closed = TRUE; - } - - Curl_uint32_hash_remove(&ctx->streams, data->mid); - } -} - -struct cf_ossq_find_ctx { - int64_t stream_id; - struct h3_stream_ctx *stream; -}; - -static bool cf_osslq_find_stream(uint32_t mid, void *val, void *user_data) -{ - struct h3_stream_ctx *stream = val; - struct cf_ossq_find_ctx *fctx = user_data; - - (void)mid; - if(stream && stream->s.id == fctx->stream_id) { - fctx->stream = stream; - return FALSE; /* stop iterating */ - } - return TRUE; -} - -static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, - struct Curl_easy *data, - int64_t stream_id) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - if(stream && stream->s.id == stream_id) { - return &stream->s; - } - else if(ctx->h3.s_ctrl.id == stream_id) { - return &ctx->h3.s_ctrl; - } - else if(ctx->h3.s_qpack_enc.id == stream_id) { - return &ctx->h3.s_qpack_enc; - } - else if(ctx->h3.s_qpack_dec.id == stream_id) { - return &ctx->h3.s_qpack_dec; - } - else { - struct cf_ossq_find_ctx fctx; - fctx.stream_id = stream_id; - fctx.stream = NULL; - Curl_uint32_hash_visit(&ctx->streams, cf_osslq_find_stream, &fctx); - if(fctx.stream) - return &fctx.stream->s; - } - return NULL; -} - -static CURLcode h3_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) -{ - (void)cf; - if(!pause) { - /* unpaused. make it run again right away */ - Curl_multi_mark_dirty(data); - } - return CURLE_OK; -} - -static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)conn; - (void)stream_id; - - /* we might be called by nghttp3 after we already cleaned up */ - if(!stream) - return 0; - - stream->closed = TRUE; - stream->error3 = app_error_code; - if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - stream->reset = TRUE; - stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, - stream->s.id, stream->error3); - } - else { - CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id); - } - Curl_multi_mark_dirty(data); - return 0; -} - -/* - * 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. - */ -static CURLcode write_resp_raw(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, size_t memlen, - bool flow) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURLcode result = CURLE_OK; - size_t nwritten; - - (void)cf; - if(!stream) { - return CURLE_RECV_ERROR; - } - result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten); - if(result) - return result; - - if(!flow) - stream->recv_buf_nonflow += nwritten; - - if(nwritten < memlen) { - /* This MUST not happen. Our recbuf is dimensioned to hold the - * full max_stream_window and then some for this reason. */ - DEBUGASSERT(0); - return CURLE_RECV_ERROR; - } - return result; -} - -static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURLcode result; - - (void)conn; - (void)stream3_id; - - if(!stream) - return NGHTTP3_ERR_CALLBACK_FAILURE; - - result = write_resp_raw(cf, data, buf, buflen, TRUE); - if(result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR %d", - stream->s.id, buflen, result); - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - stream->download_recvd += (curl_off_t)buflen; - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, total=%" FMT_OFF_T, - stream->s.id, buflen, stream->download_recvd); - Curl_multi_mark_dirty(data); - return 0; -} - -static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, - size_t consumed, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - (void)conn; - (void)stream_id; - if(stream) - CURL_TRC_CF(data, cf, "[%" PRId64 "] deferred consume %zu bytes", - stream->s.id, consumed); - return 0; -} - -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, - int32_t token, nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); - nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)token; - (void)flags; - (void)cf; - - /* we might have cleaned up this transfer already */ - if(!stream) - return 0; - - if(token == NGHTTP3_QPACK_TOKEN__STATUS) { - char line[14]; /* status line is always 13 characters long */ - size_t ncopy; - - result = Curl_http_decode_status(&stream->status_code, - (const char *)h3val.base, h3val.len); - if(result) - return -1; - ncopy = curl_msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", - stream->status_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line); - result = write_resp_raw(cf, data, line, ncopy, FALSE); - if(result) { - return -1; - } - } - else { - /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", - stream_id, (int)h3name.len, h3name.base, - (int)h3val.len, h3val.base); - result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); - if(result) { - return -1; - } - result = write_resp_raw(cf, data, ": ", 2, FALSE); - if(result) { - return -1; - } - result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE); - if(result) { - return -1; - } - result = write_resp_raw(cf, data, "\r\n", 2, FALSE); - if(result) { - return -1; - } - } - return 0; -} - -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, - int fin, void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)fin; - (void)cf; - - if(!stream) - return 0; - /* add a CRLF only if we have received some headers */ - result = write_resp_raw(cf, data, "\r\n", 2, FALSE); - if(result) { - return -1; - } - - CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", - stream_id, stream->status_code); - if(stream->status_code / 100 != 1) { - stream->resp_hds_complete = TRUE; - } - Curl_multi_mark_dirty(data); - return 0; -} - -static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)conn; - (void)app_error_code; - - if(!stream || !stream->s.ssl) - return 0; - - CURL_TRC_CF(data, cf, "[%" PRId64 "] stop_sending", stream_id); - cf_osslq_stream_close(&stream->s); - return 0; -} - -static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - int rv; - (void)conn; - - if(stream && stream->s.ssl) { - SSL_STREAM_RESET_ARGS args = { 0 }; - args.quic_error_code = app_error_code; - rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args)); - CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(!rv) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static nghttp3_ssize cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, - nghttp3_vec *vec, size_t veccnt, - uint32_t *pflags, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nwritten = 0; - size_t nvecs = 0; - (void)cf; - (void)conn; - (void)stream_id; - (void)user_data; - (void)veccnt; - - if(!stream) - return NGHTTP3_ERR_CALLBACK_FAILURE; - /* nghttp3 keeps references to the sendbuf data until it is ACKed - * by the server (see `cb_h3_acked_req_body()` for updates). - * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` - * that we have already passed to nghttp3, but which have not been - * ACKed yet. - * Any amount beyond `sendbuf_len_in_flight` we need still to pass - * to nghttp3. Do that now, if we can. */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { - nvecs = 0; - while(nvecs < veccnt && - Curl_bufq_peek_at(&stream->sendbuf, - stream->sendbuf_len_in_flight, - CURL_UNCONST(&vec[nvecs].base), - &vec[nvecs].len)) { - stream->sendbuf_len_in_flight += vec[nvecs].len; - nwritten += vec[nvecs].len; - ++nvecs; - } - DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ - } - - if(nwritten > 0 && stream->upload_left != -1) - stream->upload_left -= nwritten; - - /* When we stopped sending and everything in `sendbuf` is "in flight", - * we are at the end of the request body. */ - if(stream->upload_left == 0) { - *pflags = NGHTTP3_DATA_FLAG_EOF; - stream->send_closed = TRUE; - } - else if(!nwritten) { - /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", - stream->s.id); - return NGHTTP3_ERR_WOULDBLOCK; - } - - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", - stream->s.id, (int)nvecs, - *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", - nwritten, Curl_bufq_len(&stream->sendbuf), - stream->upload_left); - return (nghttp3_ssize)nvecs; -} - -static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - size_t skiplen; - - (void)cf; - if(!stream) - return 0; - /* The server acknowledged `datalen` of bytes from our request body. - * This is a delta. We have kept this data in `sendbuf` for - * re-transmissions and can free it now. */ - if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) - skiplen = stream->sendbuf_len_in_flight; - else - skiplen = (size_t)datalen; - Curl_bufq_skip(&stream->sendbuf, skiplen); - stream->sendbuf_len_in_flight -= skiplen; - - /* Resume upload processing if we have more data to send */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { - int rv = nghttp3_conn_resume_stream(conn, stream_id); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static nghttp3_callbacks ngh3_callbacks = { - cb_h3_acked_stream_data, - cb_h3_stream_close, - cb_h3_recv_data, - cb_h3_deferred_consume, - NULL, /* begin_headers */ - cb_h3_recv_header, - cb_h3_end_headers, - NULL, /* begin_trailers */ - cb_h3_recv_header, - NULL, /* end_trailers */ - cb_h3_stop_sending, - NULL, /* end_stream */ - cb_h3_reset_stream, - NULL, /* shutdown */ - NULL, /* recv_settings (deprecated) */ -#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ - NULL, /* recv_origin */ - NULL, /* end_origin */ - NULL, /* rand */ -#endif -#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ - NULL, /* recv_settings2 */ -#endif -}; - -static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn, - void *user_data) -{ - struct cf_osslq_h3conn *h3 = &ctx->h3; - CURLcode result; - int rc; - - nghttp3_settings_default(&h3->settings); - rc = nghttp3_conn_client_new(&h3->conn, - &ngh3_callbacks, - &h3->settings, - Curl_nghttp3_mem(), - user_data); - if(rc) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - result = cf_osslq_stream_open(&h3->s_ctrl, conn, - SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI, - &ctx->stream_bufcp, NULL); - if(result) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - result = cf_osslq_stream_open(&h3->s_qpack_enc, conn, - SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI, - &ctx->stream_bufcp, NULL); - if(result) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - result = cf_osslq_stream_open(&h3->s_qpack_dec, conn, - SSL_STREAM_FLAG_ADVANCE | SSL_STREAM_FLAG_UNI, - &ctx->stream_bufcp, NULL); - if(result) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - - rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id, - h3->s_qpack_dec.id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - - result = CURLE_OK; -out: - return result; -} - -static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result; - int rv; - const struct Curl_sockaddr_ex *peer_addr = NULL; - BIO *bio = NULL; - BIO_ADDR *baddr = NULL; - static const struct alpn_spec ALPN_SPEC_H3 = {{ "h3" }, 1}; - - DEBUGASSERT(ctx->initialized); - -#define H3_ALPN "\x2h3" - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, - &ALPN_SPEC_H3, NULL, NULL, NULL, NULL); - if(result) - goto out; - - result = vquic_ctx_init(data, &ctx->q); - if(result) - goto out; - - result = CURLE_QUIC_CONNECT_ERROR; - if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL) || - !peer_addr) - goto out; - - ctx->q.local_addrlen = sizeof(ctx->q.local_addr); - rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); - if(rv == -1) - goto out; - - result = make_bio_addr(&baddr, peer_addr); - if(result) { - failf(data, "error creating BIO_ADDR from sockaddr"); - goto out; - } - - /* Type conversions, see #12861: OpenSSL wants an `int`, but on 64-bit - * Win32 systems, Microsoft defines SOCKET as `unsigned long long`. - */ -#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) - if(ctx->q.sockfd > INT_MAX) { - failf(data, "Windows socket identifier larger than MAX_INT, " - "unable to set in OpenSSL dgram API."); - result = CURLE_QUIC_CONNECT_ERROR; - goto out; - } - bio = BIO_new_dgram((int)ctx->q.sockfd, BIO_NOCLOSE); -#else - bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE); -#endif - if(!bio) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - if(!SSL_set1_initial_peer_addr(ctx->tls.ossl.ssl, baddr)) { - failf(data, "failed to set the initial peer address"); - result = CURLE_FAILED_INIT; - goto out; - } - if(!SSL_set_blocking_mode(ctx->tls.ossl.ssl, 0)) { - failf(data, "failed to turn off blocking mode"); - result = CURLE_FAILED_INIT; - goto out; - } - - SSL_set_bio(ctx->tls.ossl.ssl, bio, bio); - bio = NULL; - SSL_set_connect_state(ctx->tls.ossl.ssl); - SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl, - SSL_INCOMING_STREAM_POLICY_ACCEPT, 0); - /* from our side, there is no idle timeout */ - SSL_set_value_uint(ctx->tls.ossl.ssl, - SSL_VALUE_CLASS_FEATURE_REQUEST, - SSL_VALUE_QUIC_IDLE_TIMEOUT, 0); - /* setup the H3 things on top of the QUIC connection */ - result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf); - -out: - if(bio) - BIO_free(bio); - if(baddr) - BIO_ADDR_free(baddr); - CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result); - return result; -} - -struct h3_quic_recv_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - struct cf_osslq_stream *s; -}; - -static CURLcode h3_quic_recv(void *reader_ctx, - unsigned char *buf, size_t len, - size_t *pnread) -{ - struct h3_quic_recv_ctx *x = reader_ctx; - int rv; - - rv = SSL_read_ex(x->s->ssl, buf, len, pnread); - if(rv <= 0) { - int detail = SSL_get_error(x->s->ssl, rv); - if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) { - return CURLE_AGAIN; - } - else if(detail == SSL_ERROR_ZERO_RETURN) { - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS", - x->s->id); - x->s->recvd_eos = TRUE; - return CURLE_OK; - } - else if(SSL_get_stream_read_state(x->s->ssl) == - SSL_STREAM_STATE_RESET_REMOTE) { - uint64_t app_error_code = NGHTTP3_H3_NO_ERROR; - if(!SSL_get_stream_read_error_code(x->s->ssl, &app_error_code)) { - x->s->reset = TRUE; - return CURLE_RECV_ERROR; - } - else { - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, " - "rv=%d, app_err=%" PRIu64, - x->s->id, rv, app_error_code); - if(app_error_code != NGHTTP3_H3_NO_ERROR) - x->s->reset = TRUE; - } - x->s->recvd_eos = TRUE; - return CURLE_OK; - } - else { - return cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR); - } - } - return CURLE_OK; -} - -static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - ssize_t nread; - size_t n; - struct h3_quic_recv_ctx x; - bool eagain = FALSE; - size_t total_recv_len = 0; - - DEBUGASSERT(s); - if(s->closed) - return CURLE_OK; - - x.cf = cf; - x.data = data; - x.s = s; - while(s->ssl && !s->closed && !eagain && - (total_recv_len < H3_STREAM_CHUNK_SIZE)) { - if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) { - while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) { - result = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &n); - if(result) { - if(result != CURLE_AGAIN) - goto out; - result = CURLE_OK; - eagain = TRUE; - } - } - } - - /* Forward what we have to nghttp3 */ - if(!Curl_bufq_is_empty(&s->recvbuf)) { - const unsigned char *buf; - size_t blen; - - while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) { - nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id, - buf, blen, 0); - CURL_TRC_CF(data, cf, "[%" PRId64 "] forward %zu bytes " - "to nghttp3 -> %zd", s->id, blen, nread); - if(nread < 0) { - failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s", - blen, nghttp3_strerror((int)nread)); - result = CURLE_RECV_ERROR; - goto out; - } - /* success, `nread` is the flow for QUIC to count as "consumed", - * not sure how that will work with OpenSSL. Anyways, without error, - * all data that we passed is not owned by nghttp3. */ - Curl_bufq_skip(&s->recvbuf, blen); - total_recv_len += blen; - } - } - - /* When we forwarded everything, handle RESET/EOS */ - if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) { - int rv; - result = CURLE_OK; - if(s->reset) { - uint64_t app_error; - if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) { - failf(data, "SSL_get_stream_read_error_code returned error"); - result = CURLE_RECV_ERROR; - goto out; - } - rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error); - s->closed = TRUE; - if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_close_stream returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_RECV_ERROR; - goto out; - } - } - else if(s->recvd_eos) { - rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, - NGHTTP3_H3_NO_ERROR); - s->closed = TRUE; - CURL_TRC_CF(data, cf, "[%" PRId64 "] close nghttp3 stream -> %d", - s->id, rv); - if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_close_stream returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_RECV_ERROR; - goto out; - } - } - } - } -out: - if(result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d", - s->id, result); - return result; -} - -struct cf_ossq_recv_ctx { - struct Curl_cfilter *cf; - struct Curl_multi *multi; - CURLcode result; -}; - -static bool cf_osslq_iter_recv(uint32_t mid, void *val, void *user_data) -{ - struct h3_stream_ctx *stream = val; - struct cf_ossq_recv_ctx *rctx = user_data; - - (void)mid; - if(stream && !stream->closed && !Curl_bufq_is_full(&stream->recvbuf)) { - struct Curl_easy *sdata = Curl_multi_get_easy(rctx->multi, mid); - if(sdata) { - rctx->result = cf_osslq_stream_recv(&stream->s, rctx->cf, sdata); - if(rctx->result) - return FALSE; /* abort iteration */ - } - } - return TRUE; -} - -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(!ctx->tls.ossl.ssl) - goto out; - - ERR_clear_error(); - - /* 1. Check for new incoming streams */ - while(1) { - SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl, - SSL_ACCEPT_STREAM_NO_BLOCK); - if(!snew) - break; - - result = cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data); - if(result) - goto out; - } - - if(!SSL_handle_events(ctx->tls.ossl.ssl)) { - int detail = SSL_get_error(ctx->tls.ossl.ssl, 0); - result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR); - if(result) - goto out; - } - - if(ctx->h3.conn) { - size_t i; - for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) { - result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data); - if(result) - goto out; - } - } - - if(ctx->h3.conn) { - struct cf_ossq_recv_ctx rctx; - - DEBUGASSERT(data->multi); - rctx.cf = cf; - rctx.multi = data->multi; - rctx.result = CURLE_OK; - Curl_uint32_hash_visit(&ctx->streams, cf_osslq_iter_recv, &rctx); - result = rctx.result; - } - -out: - CURL_TRC_CF(data, cf, "progress_ingress -> %d", result); - return result; -} - -struct cf_ossq_fill_ctx { - struct cf_osslq_ctx *ctx; - struct Curl_multi *multi; - size_t n; -}; - -static bool cf_osslq_collect_block_send(uint32_t mid, void *val, - void *user_data) -{ - struct h3_stream_ctx *stream = val; - struct cf_ossq_fill_ctx *fctx = user_data; - struct cf_osslq_ctx *ctx = fctx->ctx; - - if(fctx->n >= ctx->items_max) /* should not happen, prevent mayhem */ - return FALSE; - - if(stream && stream->s.ssl && stream->s.send_blocked) { - struct Curl_easy *sdata = Curl_multi_get_easy(fctx->multi, mid); - if(sdata) { - ctx->poll_items[fctx->n].desc = SSL_as_poll_descriptor(stream->s.ssl); - ctx->poll_items[fctx->n].events = SSL_POLL_EVENT_W; - ctx->curl_items[fctx->n] = sdata; - fctx->n++; - } - } - return TRUE; -} - -/* Iterate over all streams and check if blocked can be unblocked */ -static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream; - size_t poll_count; - size_t result_count = 0; - size_t idx_count = 0; - CURLcode res = CURLE_OK; - struct timeval timeout; - void *tmpptr; - - if(ctx->h3.conn) { - struct cf_ossq_fill_ctx fill_ctx; - - if(ctx->items_max < Curl_uint32_hash_count(&ctx->streams)) { - size_t nmax = Curl_uint32_hash_count(&ctx->streams); - ctx->items_max = 0; - tmpptr = curlx_realloc(ctx->poll_items, nmax * sizeof(SSL_POLL_ITEM)); - if(!tmpptr) { - curlx_free(ctx->poll_items); - ctx->poll_items = NULL; - res = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->poll_items = tmpptr; - - tmpptr = curlx_realloc(ctx->curl_items, - nmax * sizeof(struct Curl_easy *)); - if(!tmpptr) { - curlx_free(ctx->curl_items); - ctx->curl_items = NULL; - res = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->curl_items = tmpptr; - ctx->items_max = nmax; - } - - fill_ctx.ctx = ctx; - fill_ctx.multi = data->multi; - fill_ctx.n = 0; - Curl_uint32_hash_visit(&ctx->streams, cf_osslq_collect_block_send, - &fill_ctx); - poll_count = fill_ctx.n; - if(poll_count) { - CURL_TRC_CF(data, cf, "polling %zu blocked streams", poll_count); - - memset(&timeout, 0, sizeof(struct timeval)); - res = CURLE_UNRECOVERABLE_POLL; - if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM), - &timeout, 0, &result_count)) - goto out; - - res = CURLE_OK; - - for(idx_count = 0; idx_count < poll_count && result_count > 0; - idx_count++) { - if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) { - stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]); - DEBUGASSERT(stream); /* should still exist */ - if(stream) { - nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id); - stream->s.send_blocked = FALSE; - Curl_multi_mark_dirty(ctx->curl_items[idx_count]); - CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked"); - } - result_count--; - } - } - } - } - -out: - return res; -} - -static CURLcode h3_send_streams(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(!ctx->tls.ossl.ssl || !ctx->h3.conn) - goto out; - - for(;;) { - struct cf_osslq_stream *s = NULL; - nghttp3_vec vec[16]; - nghttp3_ssize n, i; - int64_t stream_id; - size_t written; - int eos, ok, rv; - size_t total_len, acked_len = 0; - bool blocked = FALSE, eos_written = FALSE; - - n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos, - vec, CURL_ARRAYSIZE(vec)); - if(n < 0) { - failf(data, "nghttp3_conn_writev_stream returned error: %s", - nghttp3_strerror((int)n)); - result = CURLE_SEND_ERROR; - goto out; - } - if(stream_id < 0) { - result = CURLE_OK; - goto out; - } - - /* Get the stream for this data */ - s = cf_osslq_get_qstream(cf, data, stream_id); - if(!s) { - failf(data, "nghttp3_conn_writev_stream gave unknown stream %" - PRId64, stream_id); - result = CURLE_SEND_ERROR; - goto out; - } - /* Now write the data to the stream's SSL*, it may not all fit! */ - DEBUGASSERT(s->id == stream_id); - for(i = 0, total_len = 0; i < n; ++i) { - total_len += vec[i].len; - } - for(i = 0; (i < n) && !blocked; ++i) { - /* Without stream->s.ssl, we closed that already, so - * pretend the write did succeed. */ - uint64_t flags = (eos && ((i + 1) == n)) ? SSL_WRITE_FLAG_CONCLUDE : 0; - written = vec[i].len; - ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags, - &written); - if(ok && flags & SSL_WRITE_FLAG_CONCLUDE) - eos_written = TRUE; - if(ok) { - /* As OpenSSL buffers the data, we count this as acknowledged - * from nghttp3's point of view */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] send %zu bytes to QUIC ok", - s->id, vec[i].len); - acked_len += vec[i].len; - } - else { - int detail = SSL_get_error(s->ssl, 0); - switch(detail) { - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_READ: - /* QUIC blocked us from writing more */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] send %zu bytes to " - "QUIC blocked", s->id, vec[i].len); - written = 0; - nghttp3_conn_block_stream(ctx->h3.conn, s->id); - s->send_blocked = blocked = TRUE; - break; - default: - failf(data, "[%" PRId64 "] send %zu bytes to QUIC, SSL error %d", - s->id, vec[i].len, detail); - result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3); - goto out; - } - } - } - - if(acked_len > 0 || (eos && !s->send_blocked)) { - /* Since QUIC buffers the data written internally, we can tell - * nghttp3 that it can move forward on it */ - ctx->q.last_io = *Curl_pgrs_now(data); - rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_add_write_offset returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_SEND_ERROR; - goto out; - } - rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_add_ack_offset returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_SEND_ERROR; - goto out; - } - CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes " - "to QUIC, eos=%d", s->id, acked_len, total_len, eos); - } - - if(eos && !s->send_blocked && !eos_written) { - /* wrote everything and H3 indicates end of stream */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id); - SSL_stream_conclude(s->ssl, 0); - } - } - -out: - CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result); - return result; -} - -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(!ctx->tls.ossl.ssl) - goto out; - - ERR_clear_error(); - result = h3_send_streams(cf, data); - if(result) - goto out; - - if(!SSL_handle_events(ctx->tls.ossl.ssl)) { - int detail = SSL_get_error(ctx->tls.ossl.ssl, 0); - result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR); - } - - result = cf_osslq_check_and_unblock(cf, data); - -out: - CURL_TRC_CF(data, cf, "progress_egress -> %d", result); - return result; -} - -static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct timeval tv; - timediff_t timeoutms; - int is_infinite = 1; - - if(ctx->tls.ossl.ssl && - SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite) && - !is_infinite) { - timeoutms = curlx_tvtoms(&tv); - /* QUIC want to be called again latest at the returned timeout */ - if(timeoutms <= 0) { - result = cf_progress_ingress(cf, data); - if(result) - goto out; - result = cf_progress_egress(cf, data); - if(result) - goto out; - if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) { - timeoutms = curlx_tvtoms(&tv); - } - } - if(!is_infinite) { - Curl_expire(data, timeoutms, EXPIRE_QUIC); - CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms); - } - } -out: - return result; -} - -static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - int err; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* Connect the UDP filter first */ - if(!cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; - } - - *done = FALSE; - CF_DATA_SAVE(save, cf, data); - - if(!ctx->tls.ossl.ssl) { - ctx->started_at = *Curl_pgrs_now(data); - result = cf_osslq_ctx_start(cf, data); - if(result) - goto out; - } - - if(!ctx->got_first_byte) { - int readable = SOCKET_READABLE(ctx->q.sockfd, 0); - if(readable > 0 && (readable & CURL_CSELECT_IN)) { - ctx->got_first_byte = TRUE; - ctx->first_byte_at = *Curl_pgrs_now(data); - } - } - - /* Since OpenSSL does its own send/recv internally, we may miss the - * moment to populate the x509 store right before the server response. - * Do it instead before we start the handshake, at the loss of the - * time to set this up. */ - result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); - if(result) - goto out; - - ERR_clear_error(); - err = SSL_do_handshake(ctx->tls.ossl.ssl); - - if(err == 1) { - /* connected */ - if(!ctx->got_first_byte) { - /* if not recorded yet, take the timestamp before we called - * SSL_do_handshake() as the time we received the first packet. */ - ctx->got_first_byte = TRUE; - ctx->first_byte_at = *Curl_pgrs_now(data); - } - /* Record the handshake complete with a new time stamp. */ - ctx->handshake_at = *Curl_pgrs_now(data); - ctx->q.last_io = *Curl_pgrs_now(data); - CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms", - curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started_at)); - result = cf_osslq_verify_peer(cf, data); - if(!result) { - CURL_TRC_CF(data, cf, "peer verified"); - cf->connected = TRUE; - *done = TRUE; - } - } - else { - int detail = SSL_get_error(ctx->tls.ossl.ssl, err); - switch(detail) { - case SSL_ERROR_WANT_READ: - ctx->q.last_io = *Curl_pgrs_now(data); - CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV"); - goto out; - case SSL_ERROR_WANT_WRITE: - ctx->q.last_io = *Curl_pgrs_now(data); - CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND"); - result = CURLE_OK; - goto out; -#ifdef SSL_ERROR_WANT_ASYNC - case SSL_ERROR_WANT_ASYNC: - ctx->q.last_io = *Curl_pgrs_now(data); - CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC"); - result = CURLE_OK; - goto out; -#endif -#ifdef SSL_ERROR_WANT_RETRY_VERIFY - case SSL_ERROR_WANT_RETRY_VERIFY: - result = CURLE_OK; - goto out; -#endif - default: - result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT); - goto out; - } - } - -out: - if(result == CURLE_RECV_ERROR && ctx->tls.ossl.ssl && - ctx->protocol_shutdown) { - /* When a QUIC server instance is shutting down, it may send us a - * CONNECTION_CLOSE right away. Our connection then enters the DRAINING - * state. The CONNECT may work in the near future again. Indicate - * that as a "weird" reply. */ - result = CURLE_WEIRD_SERVER_REPLY; - } - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(result) { - struct ip_quadruple ip; - - if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) - infof(data, "QUIC connect to %s port %u failed: %s", - ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); - } -#endif - if(!result) - result = check_and_set_expiry(cf, data); - if(result || *done) - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode h3_stream_open(struct Curl_cfilter *cf, - struct Curl_easy *data, - const uint8_t *buf, size_t len, - size_t *pnwritten) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = NULL; - struct dynhds h2_headers; - size_t nheader; - nghttp3_nv *nva = NULL; - int rc = 0; - unsigned int i; - nghttp3_data_reader reader; - nghttp3_data_reader *preader = NULL; - CURLcode result; - - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - - result = h3_data_setup(cf, data); - if(result) - goto out; - stream = H3_STREAM_CTX(ctx, data); - DEBUGASSERT(stream); - if(!stream) { - result = CURLE_FAILED_INIT; - goto out; - } - - result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, - !data->state.http_ignorecustom ? - data->set.str[STRING_CUSTOMREQUEST] : NULL, - 0, pnwritten); - if(result) - goto out; - if(!stream->h1.done) { - /* need more data */ - goto out; - } - DEBUGASSERT(stream->h1.req); - - result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); - if(result) - goto out; - /* no longer needed */ - Curl_h1_req_parse_free(&stream->h1); - - nheader = Curl_dynhds_count(&h2_headers); - nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP3_NV_FLAG_NONE; - } - - DEBUGASSERT(stream->s.id == -1); - result = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0, - &ctx->stream_bufcp, data); - if(result) { - failf(data, "cannot get bidi streams"); - result = CURLE_SEND_ERROR; - goto out; - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - /* known request body size or -1 */ - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown */ - break; - default: - /* there is not request body */ - stream->upload_left = 0; /* no request body */ - break; - } - - stream->send_closed = (stream->upload_left == 0); - if(!stream->send_closed) { - reader.read_data = cb_h3_read_req_body; - preader = &reader; - } - - rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id, - nva, nheader, preader, data); - if(rc) { - switch(rc) { - case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " - "connection is closing", stream->s.id); - break; - default: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)", - stream->s.id, rc, nghttp3_strerror(rc)); - break; - } - result = CURLE_SEND_ERROR; - goto out; - } - - if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", - stream->s.id, Curl_bufref_ptr(&data->state.url)); - for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", - stream->s.id, - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); - } - } - -out: - curlx_free(nva); - Curl_dynhds_free(&h2_headers); - return result; -} - -static CURLcode cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const uint8_t *buf, size_t len, bool eos, - size_t *pnwritten) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = NULL; - struct cf_call_data save; - CURLcode result = CURLE_OK; - - (void)eos; /* use to end stream */ - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->tls.ossl.ssl); - DEBUGASSERT(ctx->h3.conn); - *pnwritten = 0; - - result = cf_progress_ingress(cf, data); - if(result) - goto out; - - result = cf_progress_egress(cf, data); - if(result) - goto out; - - stream = H3_STREAM_CTX(ctx, data); - if(!stream || stream->s.id < 0) { - result = h3_stream_open(cf, data, buf, len, pnwritten); - if(result) { - CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); - goto out; - } - stream = H3_STREAM_CTX(ctx, data); - } - else if(stream->closed) { - if(stream->resp_hds_complete) { - /* Server decided to close the stream after having sent us a final - * response. This is valid if it is not interested in the request - * body. This happens on 30x or 40x responses. - * We silently discard the data sent, since this is not a transport - * error situation. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" - "on closed stream with response", stream->s.id); - result = CURLE_OK; - *pnwritten = len; - goto out; - } - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " - "-> stream closed", stream->s.id, len); - result = CURLE_HTTP3; - goto out; - } - else { - result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %d, %zu", - stream->s.id, len, result, *pnwritten); - if(result) - goto out; - (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); - } - - result = Curl_1st_err(result, cf_progress_egress(cf, data)); - -out: - result = Curl_1st_err(result, check_and_set_expiry(cf, data)); - - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", - stream ? stream->s.id : -1, len, result, *pnwritten); - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - size_t *pnread) -{ - (void)cf; - *pnread = 0; - if(stream->reset) { - failf(data, - "HTTP/3 stream %" PRId64 " reset by server", - stream->s.id); - return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; - } - else if(!stream->resp_hds_complete) { - failf(data, - "HTTP/3 stream %" PRId64 - " was closed cleanly, but before getting" - " all response header fields, treated as error", - stream->s.id); - return CURLE_HTTP3; - } - return CURLE_OK; -} - -static CURLcode cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, size_t *pnread) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream; - struct cf_call_data save; - CURLcode result = CURLE_OK; - - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->tls.ossl.ssl); - DEBUGASSERT(ctx->h3.conn); - *pnread = 0; - - stream = H3_STREAM_CTX(ctx, data); - if(!stream) { - result = CURLE_RECV_ERROR; - goto out; - } - - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); - if(result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) -> %d, %zu", - stream->s.id, len, result, *pnread); - goto out; - } - } - - result = Curl_1st_err(result, cf_progress_ingress(cf, data)); - if(result) - goto out; - - /* recvbuf had nothing before, maybe after progressing ingress? */ - if(!*pnread && !Curl_bufq_is_empty(&stream->recvbuf)) { - result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); - if(result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) -> %d, %zu", - stream->s.id, len, result, *pnread); - goto out; - } - } - - if(*pnread) { - Curl_multi_mark_dirty(data); - } - else { - if(stream->closed) { - result = recv_closed_stream(cf, data, stream, pnread); - goto out; - } - result = CURLE_AGAIN; - } - -out: - result = Curl_1st_err(result, cf_progress_egress(cf, data)); - result = Curl_1st_err(result, check_and_set_expiry(cf, data)); - - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu", - stream ? stream->s.id : -1, len, result, *pnread); - CF_DATA_RESTORE(cf, save); - return result; -} - -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -static bool cf_osslq_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - const struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)cf; - return stream && !Curl_bufq_is_empty(&stream->recvbuf); -} - -static CURLcode cf_osslq_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_DATA_SETUP: - break; - case CF_CTRL_DATA_PAUSE: - result = h3_data_pause(cf, data, (arg1 != 0)); - break; - case CF_CTRL_DATA_DONE: - h3_data_done(cf, data); - break; - case CF_CTRL_DATA_DONE_SEND: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(stream && !stream->send_closed) { - stream->send_closed = TRUE; - stream->upload_left = Curl_bufq_len(&stream->sendbuf) - - stream->sendbuf_len_in_flight; - (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); - } - break; - } - case CF_CTRL_CONN_INFO_UPDATE: - if(!cf->sockindex && cf->connected) { - cf->conn->httpversion_seen = 30; - Curl_conn_set_multiplex(cf->conn); - } - break; - default: - break; - } - CF_DATA_RESTORE(cf, save); - return result; -} - -static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - bool alive = FALSE; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - *input_pending = FALSE; - if(!ctx->tls.ossl.ssl) - goto out; - -#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT - /* Added in OpenSSL v3.3.x */ - { - timediff_t idletime; - uint64_t idle_ms = 0; - if(!SSL_get_value_uint(ctx->tls.ossl.ssl, - SSL_VALUE_CLASS_FEATURE_NEGOTIATED, - SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) { - CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, " - "assume connection is dead."); - goto out; - } - CURL_TRC_CF(data, cf, "negotiated idle timeout: %" PRIu64 "ms", idle_ms); - idletime = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->q.last_io); - if(idle_ms && idletime > 0 && (uint64_t)idletime > idle_ms) - goto out; - } - -#endif - - if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - goto out; - - alive = TRUE; - if(*input_pending) { - CURLcode result; - /* This happens before we have sent off a request and the connection is - not in use by any other transfer, there should not be any data here, - only "protocol frames" */ - *input_pending = FALSE; - result = cf_progress_ingress(cf, data); - CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); - alive = result ? FALSE : TRUE; - } - -out: - CF_DATA_RESTORE(cf, save); - return alive; -} - -static CURLcode cf_osslq_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(!ctx->tls.ossl.ssl) { - /* NOP */ - } - else if(!cf->connected) { - /* during handshake, transfer has not started yet. we always - * add our socket for polling if SSL wants to send/recv */ - result = Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ossl.ssl), - SSL_net_write_desired(ctx->tls.ossl.ssl)); - } - else { - /* once connected, we only modify the socket if it is present. - * this avoids adding it for paused transfers. */ - bool want_recv, want_send; - Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); - if(want_recv || want_send) { - result = Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ossl.ssl), - SSL_net_write_desired(ctx->tls.ossl.ssl)); - } - else if(ctx->need_recv || ctx->need_send) { - result = Curl_pollset_set(data, ps, ctx->q.sockfd, - ctx->need_recv, ctx->need_send); - } - } - return result; -} - -static CURLcode cf_osslq_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - - switch(query) { - case CF_QUERY_MAX_CONCURRENT: { -#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL - /* Added in OpenSSL v3.3.x */ - uint64_t v = 0; - if(ctx->tls.ossl.ssl && - !SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC, - SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) { - CURL_TRC_CF(data, cf, "error getting available local bidi streams"); - return CURLE_HTTP3; - } - /* we report avail + in_use */ - v += cf->conn->attached_xfers; - *pres1 = (v > INT_MAX) ? INT_MAX : (int)v; -#else - *pres1 = 100; -#endif - CURL_TRC_CF(data, cf, "query max_concurrent -> %d", *pres1); - return CURLE_OK; - } - case CF_QUERY_CONNECT_REPLY_MS: - if(ctx->got_first_byte) { - timediff_t ms = curlx_ptimediff_ms(&ctx->first_byte_at, - &ctx->started_at); - *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; - } - 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; - } - case CF_QUERY_HTTP_VERSION: - *pres1 = 30; - return CURLE_OK; - case CF_QUERY_SSL_INFO: - case CF_QUERY_SSL_CTX_INFO: { - struct curl_tlssessioninfo *info = pres2; - if(Curl_vquic_tls_get_ssl_info(&ctx->tls, - (query == CF_QUERY_SSL_CTX_INFO), info)) - return CURLE_OK; - break; - } - case CF_QUERY_ALPN_NEGOTIATED: { - const char **palpn = pres2; - DEBUGASSERT(palpn); - *palpn = cf->connected ? "h3" : NULL; - return CURLE_OK; - } - default: - break; - } - return cf->next ? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -struct Curl_cftype Curl_cft_http3 = { - "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, - 0, - cf_osslq_destroy, - cf_osslq_connect, - cf_osslq_close, - cf_osslq_shutdown, - cf_osslq_adjust_pollset, - cf_osslq_data_pending, - cf_osslq_send, - cf_osslq_recv, - cf_osslq_cntrl, - cf_osslq_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_osslq_query, -}; - -CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai) -{ - struct cf_osslq_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL; - CURLcode result; - - (void)data; - ctx = curlx_calloc(1, sizeof(*ctx)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - cf_osslq_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); - if(result) - goto out; - cf->conn = conn; - - result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC); - if(result) - goto out; - - cf->next->conn = cf->conn; - cf->next->sockindex = cf->sockindex; - -out: - *pcf = (!result) ? cf : NULL; - if(result) { - if(cf) - Curl_conn_cf_discard_chain(&cf, data); - else if(ctx) - cf_osslq_ctx_free(ctx); - } - return result; -} - -bool Curl_conn_is_osslq(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL; - - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_http3) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -/* - * Store ngtcp2 version info in this buffer. - */ -void Curl_osslq_ver(char *p, size_t len) -{ - const nghttp3_info *ht3 = nghttp3_version(0); - (void)curl_msnprintf(p, len, "nghttp3/%s", ht3->version_str); -} - -#endif /* !CURL_DISABLE_HTTP && USE_OPENSSL_QUIC && USE_NGHTTP3 */ diff --git a/vendor/curl/lib/vquic/curl_quiche.c b/vendor/curl/lib/vquic/curl_quiche.c index e4140e7..d8e6b30 100644 --- a/vendor/curl/lib/vquic/curl_quiche.c +++ b/vendor/curl/lib/vquic/curl_quiche.c @@ -21,49 +21,46 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_QUICHE) #include #include #include -#include "../bufq.h" -#include "../uint-hash.h" -#include "../urldata.h" -#include "../cfilters.h" -#include "../cf-socket.h" -#include "../curl_trc.h" -#include "../rand.h" -#include "../multiif.h" -#include "../connect.h" -#include "../progress.h" -#include "../select.h" -#include "../http1.h" -#include "vquic.h" -#include "vquic_int.h" -#include "vquic-tls.h" -#include "curl_quiche.h" -#include "../transfer.h" -#include "../url.h" -#include "../bufref.h" -#include "../vtls/openssl.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" + +#include "bufq.h" +#include "uint-hash.h" +#include "urldata.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "curl_trc.h" +#include "rand.h" +#include "multiif.h" +#include "connect.h" +#include "progress.h" +#include "select.h" +#include "http1.h" +#include "vquic/vquic.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" +#include "vquic/curl_quiche.h" +#include "transfer.h" +#include "url.h" +#include "bufref.h" +#include "vtls/openssl.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" /* HTTP/3 error values defined in RFC 9114, ch. 8.1 */ -#define CURL_H3_NO_ERROR (0x0100) - -#define QUIC_MAX_STREAMS (100) - -#define H3_STREAM_WINDOW_SIZE (128 * 1024) -#define H3_STREAM_CHUNK_SIZE (16 * 1024) -/* The pool keeps spares around and half of a full stream windows seems good. - * More does not seem to improve performance. The benefit of the pool is that - * stream buffer to not keep spares. Memory consumption goes down when streams - * run empty, have a large upload done, etc. */ -#define H3_STREAM_POOL_SPARES \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) / 2 -/* Receive and Send max number of chunks just follows from the +#define CURL_H3_NO_ERROR 0x0100 + +#define MAX_PKT_BURST 10 + +#define QUIC_MAX_STREAMS 100 + +#define H3_STREAM_WINDOW_SIZE (1024 * 128) +#define H3_STREAM_CHUNK_SIZE (1024 * 16) +/* Receive and Send max number of chunks follows from the * chunk size and window size */ #define H3_STREAM_RECV_CHUNKS \ (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) @@ -129,7 +126,7 @@ static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) { if(ctx && ctx->initialized) { - /* quiche just freed it */ + /* quiche freed it */ ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); Curl_ssl_peer_cleanup(&ctx->peer); @@ -340,7 +337,7 @@ static void cf_quiche_write_hd(struct Curl_cfilter *cf, if(!stream->xfer_result) { stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); if(stream->xfer_result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu " + CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu " "bytes of headers", stream->id, stream->xfer_result, blen); } } @@ -392,14 +389,14 @@ static int cb_each_header(uint8_t *name, size_t name_len, if(!result) cf_quiche_write_hd(cf, data, stream, curlx_dyn_ptr(&ctx->h1hdr), curlx_dyn_len(&ctx->h1hdr), FALSE); - CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", + CURL_TRC_CF(data, cf, "[%" PRIu64 "] status: %s", stream->id, curlx_dyn_ptr(&ctx->h1hdr)); } else { if(is_valid_h3_header(value, value_len) && is_valid_h3_header(name, name_len)) { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" PRIu64 "] header: %.*s: %.*s", stream->id, (int)name_len, name, (int)value_len, value); curlx_dyn_reset(&ctx->h1hdr); @@ -461,7 +458,7 @@ static void cf_quiche_flush_body(struct Curl_cfilter *cf, data, (const char *)buf, blen, FALSE); Curl_bufq_skip(&ctx->writebuf, blen); if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu bytes" + CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu bytes" " of data", stream->id, stream->xfer_result, blen); } } @@ -855,7 +852,23 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, DEBUGASSERT(stream); *pnread = 0; if(stream->reset) { - failf(data, "HTTP/3 stream %" PRIu64 " reset by server", stream->id); + if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { + infof(data, "HTTP/3 stream %" PRIu64 " refused by server, try again " + "on a new connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + data->state.refused_stream = TRUE; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ + } + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%" PRIu64 "] error after response headers, " + "but we did not want a body anyway, ignore error 0x%" + PRIx64 " %s", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + return CURLE_OK; + } + failf(data, "HTTP/3 stream %" PRIu64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv, was reset -> %d", stream->id, result); @@ -1066,7 +1079,7 @@ static CURLcode h3_open_stream(struct Curl_cfilter *cf, CURLcode r2 = CURLE_OK; r2 = cf_quiche_send_body(cf, data, stream, buf, blen, eos, &nwritten); - if(r2 && (CURLE_AGAIN != r2)) { /* real error, fail */ + if(r2 && (r2 != CURLE_AGAIN)) { /* real error, fail */ result = r2; } else if(nwritten > 0) { @@ -1111,7 +1124,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, * server. If the server has send us a final response, we should * silently discard the send data. * This happens for example on redirects where the server, instead - * of reading the full request body just closed the stream after + * of reading the full request body closed the stream after * sending the 30x response. * This is sort of a race: had the transfer loop called recv first, * it would see the response and stop/discard sending on its own- */ @@ -1418,7 +1431,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, } out: -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(result && result != CURLE_AGAIN) { struct ip_quadruple ip; @@ -1627,8 +1640,6 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_cfilter *cf = NULL; CURLcode result; - (void)data; - (void)conn; ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; diff --git a/vendor/curl/lib/vquic/curl_quiche.h b/vendor/curl/lib/vquic/curl_quiche.h index 6c0fb9e..fb5a563 100644 --- a/vendor/curl/lib/vquic/curl_quiche.h +++ b/vendor/curl/lib/vquic/curl_quiche.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_QUICHE) diff --git a/vendor/curl/lib/vquic/vquic-tls.c b/vendor/curl/lib/vquic/vquic-tls.c index 3391d98..e135ac5 100644 --- a/vendor/curl/lib/vquic/vquic-tls.c +++ b/vendor/curl/lib/vquic/vquic-tls.c @@ -21,34 +21,34 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) #ifdef USE_OPENSSL #include -#include "../vtls/openssl.h" +#include "vtls/openssl.h" #elif defined(USE_GNUTLS) #include #include #include #include #include -#include "../vtls/gtls.h" +#include "vtls/gtls.h" #elif defined(USE_WOLFSSL) #include #include #include -#include "../vtls/wolfssl.h" +#include "vtls/wolfssl.h" #endif -#include "../urldata.h" -#include "../cfilters.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" -#include "../vtls/vtls_scache.h" -#include "vquic-tls.h" +#include "urldata.h" +#include "cfilters.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" +#include "vtls/vtls_scache.h" +#include "vquic/vquic-tls.h" CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, diff --git a/vendor/curl/lib/vquic/vquic-tls.h b/vendor/curl/lib/vquic/vquic-tls.h index cff7654..33adec2 100644 --- a/vendor/curl/lib/vquic/vquic-tls.h +++ b/vendor/curl/lib/vquic/vquic-tls.h @@ -23,17 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) -#include "../bufq.h" -#include "../vtls/vtls.h" -#include "../vtls/vtls_int.h" +#include "bufq.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" -#include "../vtls/openssl.h" -#include "../vtls/wolfssl.h" +#include "vtls/openssl.h" +#include "vtls/wolfssl.h" struct ssl_peer; struct Curl_ssl_session; @@ -53,7 +53,7 @@ struct curl_tls_ctx { * Callback passed to `Curl_vquic_tls_init()` that can * do early initializations on the not otherwise configured TLS * instances created. This varies by TLS backend: - * - openssl/wolfssl: SSL_CTX* has just been created + * - openssl/wolfssl: SSL_CTX* has been created * - gnutls: gtls_client_init() has run */ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, diff --git a/vendor/curl/lib/vquic/vquic.c b/vendor/curl/lib/vquic/vquic.c index d7bfa99..1eaabb9 100644 --- a/vendor/curl/lib/vquic/vquic.c +++ b/vendor/curl/lib/vquic/vquic.c @@ -21,33 +21,35 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" +#include "urldata.h" +#include "vquic/vquic.h" + +#include "curl_trc.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) #ifdef HAVE_NETINET_UDP_H #include #endif + #ifdef USE_NGHTTP3 #include #endif -#include "../urldata.h" -#include "../bufq.h" -#include "../curlx/dynbuf.h" -#include "../curlx/fopen.h" -#include "../cfilters.h" -#include "../curl_trc.h" -#include "curl_ngtcp2.h" -#include "curl_osslq.h" -#include "curl_quiche.h" -#include "../multiif.h" -#include "../progress.h" -#include "../rand.h" -#include "vquic.h" -#include "vquic_int.h" -#include "../curlx/strerr.h" -#include "../curlx/strparse.h" +#include "bufq.h" +#include "curlx/dynbuf.h" +#include "curlx/fopen.h" +#include "cfilters.h" +#include "vquic/curl_ngtcp2.h" +#include "vquic/curl_quiche.h" +#include "multiif.h" +#include "progress.h" +#include "rand.h" +#include "vquic/vquic_int.h" +#include "curlx/strerr.h" +#include "curlx/strparse.h" -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) #define NW_CHUNK_SIZE (64 * 1024) #define NW_SEND_CHUNKS 1 @@ -66,8 +68,6 @@ void Curl_quic_ver(char *p, size_t len) { #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) Curl_ngtcp2_ver(p, len); -#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) - Curl_osslq_ver(p, len); #elif defined(USE_QUICHE) Curl_quiche_ver(p, len); #endif @@ -170,7 +170,8 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, #endif return CURLE_AGAIN; case SOCKEMSGSIZE: - /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ + /* UDP datagram is too large; caused by PMTUD. Let it be lost. */ + *psent = pktlen; break; case EIO: if(pktlen > gsolen) { @@ -198,8 +199,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, *psent = 0; - while((rv = send(qctx->sockfd, (const char *)pkt, - (SEND_TYPE_ARG3)pktlen, 0)) == -1 && + while((rv = swrite(qctx->sockfd, pkt, pktlen)) == -1 && SOCKERRNO == SOCKEINTR) ; @@ -209,13 +209,13 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, goto out; } else { - failf(data, "send() returned %zd (errno %d)", rv, SOCKERRNO); if(SOCKERRNO != SOCKEMSGSIZE) { + failf(data, "send() returned %zd (errno %d)", rv, SOCKERRNO); result = CURLE_SEND_ERROR; goto out; } - /* UDP datagram is too large; caused by PMTUD. Just let it be - lost. */ + /* UDP datagram is too large; caused by PMTUD. Let it be lost. */ + *psent = pktlen; } } #endif @@ -225,11 +225,13 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, return result; } +#ifdef CURLVERBOSE #ifdef HAVE_SENDMSG #define VQUIC_SEND_METHOD "sendmsg" #else #define VQUIC_SEND_METHOD "send" #endif +#endif static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -238,8 +240,9 @@ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, size_t gsolen, size_t *psent) { const uint8_t *p, *end = pkt + pktlen; - size_t sent, len, calls = 0; + size_t sent, len; CURLcode result = CURLE_OK; + VERBOSE(size_t calls = 0); *psent = 0; @@ -249,7 +252,7 @@ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, if(result) goto out; *psent += sent; - ++calls; + VERBOSE(++calls); } out: CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=%zu)" @@ -392,7 +395,10 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct mmsghdr mmsg[MMSG_NUM]; uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))]; struct sockaddr_storage remote_addr[MMSG_NUM]; - size_t total_nread = 0, pkts = 0, calls = 0; + size_t total_nread = 0, pkts = 0; +#ifdef CURLVERBOSE + size_t calls = 0; +#endif int mcount, i, n; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; @@ -445,7 +451,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, goto out; } - ++calls; + VERBOSE(++calls); for(i = 0; i < mcount; ++i) { /* A zero-length UDP packet is no QUIC packet. Ignore. */ if(!mmsg[i].msg_len) @@ -703,8 +709,6 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, DEBUGASSERT(transport == TRNSPRT_QUIC); #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) return Curl_cf_ngtcp2_create(pcf, data, conn, ai); -#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) - return Curl_cf_osslq_create(pcf, data, conn, ai); #elif defined(USE_QUICHE) return Curl_cf_quiche_create(pcf, data, conn, ai); #else @@ -724,7 +728,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, /* cannot do QUIC over a Unix domain socket */ return CURLE_QUIC_CONNECT_ERROR; } - if(!(conn->handler->flags & PROTOPT_SSL)) { + if(!(conn->scheme->flags & PROTOPT_SSL)) { failf(data, "HTTP/3 requested for non-HTTPS URL"); return CURLE_URL_MALFORMAT; } @@ -742,6 +746,56 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, return CURLE_OK; } +#ifdef CURLVERBOSE +const char *vquic_h3_err_str(uint64_t error_code) +{ + if(error_code <= UINT_MAX) { + switch((unsigned int)error_code) { + case CURL_H3_ERR_NO_ERROR: + return "NO_ERROR"; + case CURL_H3_ERR_GENERAL_PROTOCOL_ERROR: + return "GENERAL_PROTOCOL_ERROR"; + case CURL_H3_ERR_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case CURL_H3_ERR_STREAM_CREATION_ERROR: + return "STREAM_CREATION_ERROR"; + case CURL_H3_ERR_CLOSED_CRITICAL_STREAM: + return "CLOSED_CRITICAL_STREAM"; + case CURL_H3_ERR_FRAME_UNEXPECTED: + return "FRAME_UNEXPECTED"; + case CURL_H3_ERR_FRAME_ERROR: + return "FRAME_ERROR"; + case CURL_H3_ERR_EXCESSIVE_LOAD: + return "EXCESSIVE_LOAD"; + case CURL_H3_ERR_ID_ERROR: + return "ID_ERROR"; + case CURL_H3_ERR_SETTINGS_ERROR: + return "SETTINGS_ERROR"; + case CURL_H3_ERR_MISSING_SETTINGS: + return "MISSING_SETTINGS"; + case CURL_H3_ERR_REQUEST_REJECTED: + return "REQUEST_REJECTED"; + case CURL_H3_ERR_REQUEST_CANCELLED: + return "REQUEST_CANCELLED"; + case CURL_H3_ERR_REQUEST_INCOMPLETE: + return "REQUEST_INCOMPLETE"; + case CURL_H3_ERR_MESSAGE_ERROR: + return "MESSAGE_ERROR"; + case CURL_H3_ERR_CONNECT_ERROR: + return "CONNECT_ERROR"; + case CURL_H3_ERR_VERSION_FALLBACK: + return "VERSION_FALLBACK"; + default: + break; + } + } + /* RFC 9114 ch. 8.1 + 9, reserved future error codes that are NO_ERROR */ + if((error_code >= 0x21) && !((error_code - 0x21) % 0x1f)) + return "NO_ERROR"; + return "unknown"; +} +#endif /* CURLVERBOSE */ + #if defined(USE_NGTCP2) || defined(USE_NGHTTP3) static void *vquic_ngtcp2_malloc(size_t size, void *user_data) @@ -804,8 +858,8 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn, unsigned char transport) { - (void)conn; (void)data; + (void)conn; (void)transport; DEBUGF(infof(data, "QUIC is not supported in this build")); return CURLE_NOT_BUILT_IN; diff --git a/vendor/curl/lib/vquic/vquic.h b/vendor/curl/lib/vquic/vquic.h index f5392eb..528e184 100644 --- a/vendor/curl/lib/vquic/vquic.h +++ b/vendor/curl/lib/vquic/vquic.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) struct Curl_cfilter; diff --git a/vendor/curl/lib/vquic/vquic_int.h b/vendor/curl/lib/vquic/vquic_int.h index e602b7e..82bd5b0 100644 --- a/vendor/curl/lib/vquic/vquic_int.h +++ b/vendor/curl/lib/vquic/vquic_int.h @@ -23,15 +23,41 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" - -#include "../bufq.h" +#include "curl_setup.h" #ifdef USE_HTTP3 -#define MAX_PKT_BURST 10 +#include "bufq.h" + #define MAX_UDP_PAYLOAD_SIZE 1452 +/* definitions from RFC 9114, ch 8.1 */ +typedef enum { + CURL_H3_ERR_NO_ERROR = 0x0100, + CURL_H3_ERR_GENERAL_PROTOCOL_ERROR = 0x0101, + CURL_H3_ERR_INTERNAL_ERROR = 0x0102, + CURL_H3_ERR_STREAM_CREATION_ERROR = 0x0103, + CURL_H3_ERR_CLOSED_CRITICAL_STREAM = 0x0104, + CURL_H3_ERR_FRAME_UNEXPECTED = 0x0105, + CURL_H3_ERR_FRAME_ERROR = 0x0106, + CURL_H3_ERR_EXCESSIVE_LOAD = 0x0107, + CURL_H3_ERR_ID_ERROR = 0x0108, + CURL_H3_ERR_SETTINGS_ERROR = 0x0109, + CURL_H3_ERR_MISSING_SETTINGS = 0x010a, + CURL_H3_ERR_REQUEST_REJECTED = 0x010b, + CURL_H3_ERR_REQUEST_CANCELLED = 0x010c, + CURL_H3_ERR_REQUEST_INCOMPLETE = 0x010d, + CURL_H3_ERR_MESSAGE_ERROR = 0x010e, + CURL_H3_ERR_CONNECT_ERROR = 0x010f, + CURL_H3_ERR_VERSION_FALLBACK = 0x0110, +} vquic_h3_error; + +#ifdef CURLVERBOSE +const char *vquic_h3_err_str(uint64_t error_code); +#else +#define vquic_h3_err_str(x) "" +#endif /* CURLVERBOSE */ + struct cf_quic_ctx { curl_socket_t sockfd; /* connected UDP socket */ struct sockaddr_storage local_addr; /* address socket is bound to */ @@ -52,7 +78,7 @@ struct cf_quic_ctx { }; #define H3_STREAM_CTX(ctx, data) \ - (data ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) + ((data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) CURLcode vquic_ctx_init(struct Curl_easy *data, struct cf_quic_ctx *qctx); @@ -94,8 +120,6 @@ CURLcode vquic_recv_packets(struct Curl_cfilter *cf, size_t max_pkts, vquic_recv_pkts_cb *recv_cb, void *userp); -#endif /* !USE_HTTP3 */ - #ifdef USE_NGTCP2 struct ngtcp2_mem; struct ngtcp2_mem *Curl_ngtcp2_mem(void); @@ -105,4 +129,6 @@ struct nghttp3_mem; struct nghttp3_mem *Curl_nghttp3_mem(void); #endif +#endif /* !USE_HTTP3 */ + #endif /* HEADER_CURL_VQUIC_QUIC_INT_H */ diff --git a/vendor/curl/lib/vssh/libssh.c b/vendor/curl/lib/vssh/libssh.c index 3e6cb3a..3a2a52e 100644 --- a/vendor/curl/lib/vssh/libssh.c +++ b/vendor/curl/lib/vssh/libssh.c @@ -24,7 +24,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_LIBSSH @@ -42,21 +42,21 @@ #include #endif -#include "../urldata.h" -#include "../sendf.h" -#include "../curl_trc.h" -#include "../hostip.h" -#include "../progress.h" -#include "../transfer.h" -#include "ssh.h" -#include "../url.h" -#include "../cfilters.h" -#include "../connect.h" -#include "../parsedate.h" /* for the week day and month names */ -#include "../curlx/strparse.h" -#include "../multiif.h" -#include "../select.h" -#include "vssh.h" +#include "urldata.h" +#include "sendf.h" +#include "curl_trc.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "vssh/ssh.h" +#include "url.h" +#include "cfilters.h" +#include "connect.h" +#include "parsedate.h" /* for the week day and month names */ +#include "curlx/strparse.h" +#include "multiif.h" +#include "select.h" +#include "vssh/vssh.h" #ifdef HAVE_UNISTD_H #include @@ -84,100 +84,6 @@ #define SSH_S_IFLNK 0120000 #endif -/* Local functions: */ -static CURLcode myssh_connect(struct Curl_easy *data, bool *done); -static CURLcode myssh_multi_statemach(struct Curl_easy *data, - bool *done); -static CURLcode myssh_do_it(struct Curl_easy *data, bool *done); - -static CURLcode scp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode scp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); - -static CURLcode sftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode sftp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode sftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead); -static CURLcode sftp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done); - -static CURLcode myssh_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static void myssh_block2waitfor(struct connectdata *conn, - struct ssh_conn *sshc, - bool block); - -static CURLcode myssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static void sshc_cleanup(struct ssh_conn *sshc); - -/* - * SCP protocol handler. - */ - -const struct Curl_handler Curl_handler_scp = { - "SCP", /* scheme */ - myssh_setup_connection, /* setup_connection */ - myssh_do_it, /* do_it */ - scp_done, /* done */ - ZERO_NULL, /* do_more */ - myssh_connect, /* connect_it */ - myssh_multi_statemach, /* connecting */ - scp_doing, /* doing */ - myssh_pollset, /* proto_pollset */ - myssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - myssh_pollset, /* perform_pollset */ - scp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SSH, /* defport */ - CURLPROTO_SCP, /* protocol */ - CURLPROTO_SCP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE -}; - -/* - * SFTP protocol handler. - */ - -const struct Curl_handler Curl_handler_sftp = { - "SFTP", /* scheme */ - myssh_setup_connection, /* setup_connection */ - myssh_do_it, /* do_it */ - sftp_done, /* done */ - ZERO_NULL, /* do_more */ - myssh_connect, /* connect_it */ - myssh_multi_statemach, /* connecting */ - sftp_doing, /* doing */ - myssh_pollset, /* proto_pollset */ - myssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - myssh_pollset, /* perform_pollset */ - sftp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ - PORT_SSH, /* defport */ - CURLPROTO_SFTP, /* protocol */ - CURLPROTO_SFTP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE -}; - static CURLcode sftp_error_to_CURLE(int err) { switch(err) { @@ -202,101 +108,6 @@ static CURLcode sftp_error_to_CURLE(int err) return CURLE_SSH; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static const char *myssh_statename(sshstate state) -{ - static const char * const names[] = { - "SSH_STOP", - "SSH_INIT", - "SSH_S_STARTUP", - "SSH_HOSTKEY", - "SSH_AUTHLIST", - "SSH_AUTH_PKEY_INIT", - "SSH_AUTH_PKEY", - "SSH_AUTH_PASS_INIT", - "SSH_AUTH_PASS", - "SSH_AUTH_AGENT_INIT", - "SSH_AUTH_AGENT_LIST", - "SSH_AUTH_AGENT", - "SSH_AUTH_HOST_INIT", - "SSH_AUTH_HOST", - "SSH_AUTH_KEY_INIT", - "SSH_AUTH_KEY", - "SSH_AUTH_GSSAPI", - "SSH_AUTH_DONE", - "SSH_SFTP_INIT", - "SSH_SFTP_REALPATH", - "SSH_SFTP_QUOTE_INIT", - "SSH_SFTP_POSTQUOTE_INIT", - "SSH_SFTP_QUOTE", - "SSH_SFTP_NEXT_QUOTE", - "SSH_SFTP_QUOTE_STAT", - "SSH_SFTP_QUOTE_SETSTAT", - "SSH_SFTP_QUOTE_SYMLINK", - "SSH_SFTP_QUOTE_MKDIR", - "SSH_SFTP_QUOTE_RENAME", - "SSH_SFTP_QUOTE_RMDIR", - "SSH_SFTP_QUOTE_UNLINK", - "SSH_SFTP_QUOTE_STATVFS", - "SSH_SFTP_GETINFO", - "SSH_SFTP_FILETIME", - "SSH_SFTP_TRANS_INIT", - "SSH_SFTP_UPLOAD_INIT", - "SSH_SFTP_CREATE_DIRS_INIT", - "SSH_SFTP_CREATE_DIRS", - "SSH_SFTP_CREATE_DIRS_MKDIR", - "SSH_SFTP_READDIR_INIT", - "SSH_SFTP_READDIR", - "SSH_SFTP_READDIR_LINK", - "SSH_SFTP_READDIR_BOTTOM", - "SSH_SFTP_READDIR_DONE", - "SSH_SFTP_DOWNLOAD_INIT", - "SSH_SFTP_DOWNLOAD_STAT", - "SSH_SFTP_CLOSE", - "SSH_SFTP_SHUTDOWN", - "SSH_SCP_TRANS_INIT", - "SSH_SCP_UPLOAD_INIT", - "SSH_SCP_DOWNLOAD_INIT", - "SSH_SCP_DOWNLOAD", - "SSH_SCP_DONE", - "SSH_SCP_SEND_EOF", - "SSH_SCP_WAIT_EOF", - "SSH_SCP_WAIT_CLOSE", - "SSH_SCP_CHANNEL_FREE", - "SSH_SESSION_DISCONNECT", - "SSH_SESSION_FREE", - "QUIT" - }; - /* a precaution to make sure the lists are in sync */ - DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST); - return ((size_t)state < CURL_ARRAYSIZE(names)) ? names[state] : ""; -} -#else -#define myssh_statename(x) "" -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ - -#define myssh_to(x, y, z) myssh_set_state(x, y, z) - -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void myssh_set_state(struct Curl_easy *data, - struct ssh_conn *sshc, - sshstate nowstate) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(sshc->state != nowstate) { - CURL_TRC_SSH(data, "[%s] -> [%s]", - myssh_statename(sshc->state), - myssh_statename(nowstate)); - } -#else - (void)data; -#endif - sshc->state = nowstate; -} - /* Multiple options: * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 * hash (90s style auth, not sure we should have it here) @@ -341,7 +152,7 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) } for(i = 0; i < 16; i++) - curl_msnprintf(&md5buffer[i * 2], 3, "%02x", (unsigned char)hash[i]); + curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash[i]); infof(data, "SSH MD5 fingerprint: %s", md5buffer); @@ -727,6 +538,19 @@ static int myssh_in_SFTP_READDIR_DONE(struct Curl_easy *data, return SSH_NO_ERROR; } +static void myssh_quote_error(struct Curl_easy *data, struct ssh_conn *sshc, + const char *cmd) +{ + if(cmd) + failf(data, "%s command failed: %s", cmd, + ssh_get_error(sshc->ssh_session)); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; +} + static int myssh_in_SFTP_QUOTE_STATVFS(struct Curl_easy *data, struct ssh_conn *sshc) { @@ -734,20 +558,15 @@ static int myssh_in_SFTP_QUOTE_STATVFS(struct Curl_easy *data, statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); if(!statvfs && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "statvfs command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "statvfs"); return SSH_OK; } else if(statvfs) { - #ifdef _MSC_VER - #define CURL_LIBSSH_VFS_SIZE_MASK "I64u" - #else - #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 - #endif +#ifdef _MSC_VER +#define CURL_LIBSSH_VFS_SIZE_MASK "I64u" +#else +#define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 +#endif CURLcode result = CURLE_OK; char *tmp = curl_maprintf("statvfs:\n" "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" @@ -868,6 +687,22 @@ static void myssh_state_init(struct Curl_easy *data, myssh_to(data, sshc, SSH_S_STARTUP); } +static void myssh_block2waitfor(struct connectdata *conn, + struct ssh_conn *sshc, + bool block) +{ + (void)conn; + if(block) { + int dir = ssh_get_poll_flags(sshc->ssh_session); + /* translate the libssh define bits into our own bit defines */ + sshc->waitfor = + ((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) | + ((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0); + } + else + sshc->waitfor = 0; +} + static int myssh_in_S_STARTUP(struct Curl_easy *data, struct ssh_conn *sshc) { @@ -914,13 +749,13 @@ static int myssh_in_AUTHLIST(struct Curl_easy *data, if(sshc->auth_methods) infof(data, "SSH authentication methods available: %s%s%s%s", sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ? - "public key, ": "", + "public key, " : "", sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ? "GSSAPI, " : "", sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ? "keyboard-interactive, " : "", sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ? - "password": ""); + "password" : ""); /* For public key auth we need either the private key or CURLSSH_AUTH_AGENT. */ if((sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) && @@ -1104,7 +939,7 @@ static int myssh_in_AUTH_DONE(struct Curl_easy *data, conn->recv_idx = FIRSTSOCKET; conn->send_idx = -1; - if(conn->handler->protocol == CURLPROTO_SFTP) { + if(conn->scheme->protocol == CURLPROTO_SFTP) { myssh_to(data, sshc, SSH_SFTP_INIT); return SSH_NO_ERROR; } @@ -1165,10 +1000,11 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, if(!sshc->sftp_file) { int err = sftp_get_error(sshc->sftp_session); - if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE || - err == SSH_FX_NO_SUCH_PATH)) && - (data->set.ftp_create_missing_dirs && - (strlen(sshp->path) > 1))) { + if((err == SSH_FX_NO_SUCH_FILE || + err == SSH_FX_FAILURE || + err == SSH_FX_NO_SUCH_PATH) && + data->set.ftp_create_missing_dirs && + (strlen(sshp->path) > 1)) { /* try to create the path remotely */ rc = 0; sshc->secondCreateDirs = 1; @@ -1293,7 +1129,7 @@ static int myssh_in_SFTP_DOWNLOAD_STAT(struct Curl_easy *data, (attrs->size == 0)) { /* * sftp_fstat did not return an error, so maybe the server - * just does not support stat() + * does not support stat() * OR the server does not return a file size with a stat() * OR file size is 0 */ @@ -1333,7 +1169,7 @@ static int myssh_in_SFTP_DOWNLOAD_STAT(struct Curl_easy *data, if(data->state.resume_from) { if(data->state.resume_from < 0) { /* We are supposed to download the last abs(from) bytes */ - if((curl_off_t)size < -data->state.resume_from) { + if(size < -data->state.resume_from) { failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, size); return myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); @@ -1342,7 +1178,7 @@ static int myssh_in_SFTP_DOWNLOAD_STAT(struct Curl_easy *data, data->state.resume_from += size; } else { - if((curl_off_t)size < data->state.resume_from) { + if(size < data->state.resume_from) { failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, size); @@ -1392,7 +1228,7 @@ static int myssh_in_SFTP_CLOSE(struct Curl_easy *data, /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT After nextstate is executed, the control should come back to - SSH_SFTP_CLOSE to pass the correct result back */ + SSH_SFTP_CLOSE to pass the correct result back */ if(sshc->nextstate != SSH_NO_STATE && sshc->nextstate != SSH_SFTP_CLOSE) { myssh_to(data, sshc, sshc->nextstate); @@ -1532,7 +1368,7 @@ static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, /* * Support some of the "FTP" commands */ - char *cmd = sshc->quote_item->data; + const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never @@ -1743,7 +1579,7 @@ static int myssh_in_SFTP_NEXT_QUOTE(struct Curl_easy *data, static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, struct ssh_conn *sshc) { - char *cmd = sshc->quote_item->data; + const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never @@ -1765,13 +1601,9 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, sftp_attributes_free(sshc->quote_attrs); sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); if(!sshc->quote_attrs) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Attempt to get SFTP stats failed: %d", sftp_get_error(sshc->sftp_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } @@ -1780,12 +1612,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, const char *p = sshc->quote_path1; curl_off_t gid; if(curlx_str_number(&p, &gid, UINT_MAX)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Syntax error: chgrp gid not a number"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->gid = (uint32_t)gid; @@ -1795,12 +1623,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, curl_off_t perms; const char *p = sshc->quote_path1; if(curlx_str_octal(&p, &perms, 07777)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Syntax error: chmod permissions not a number"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->permissions = (mode_t)perms; @@ -1810,12 +1634,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, const char *p = sshc->quote_path1; curl_off_t uid; if(curlx_str_number(&p, &uid, UINT_MAX)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Syntax error: chown uid not a number"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->uid = (uint32_t)uid; @@ -1836,11 +1656,7 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, } #endif if(fail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } if(date > UINT_MAX) @@ -1871,6 +1687,160 @@ static void conn_forget_socket(struct Curl_easy *data, int sockindex) } } +static void myssh_SESSION_DISCONNECT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + /* during weird times when we have been prematurely aborted, the channel + is still alive when we reach this state and we MUST kill the channel + properly first */ + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + if(sshc->sftp_session) { + sftp_free(sshc->sftp_session); + sshc->sftp_session = NULL; + } + + ssh_disconnect(sshc->ssh_session); + if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { + /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, + tell the connection to forget about it. This libssh + bug is fixed in 0.10.0. */ + conn_forget_socket(data, FIRSTSOCKET); + } + + SSH_STRING_FREE_CHAR(sshc->homedir); + + myssh_to(data, sshc, SSH_SESSION_FREE); +} + +static int myssh_SSH_SCP_DOWNLOAD(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + curl_off_t bytecount; + int rc = ssh_scp_pull_request(sshc->scp_session); + if(rc != SSH_SCP_REQUEST_NEWFILE) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND); + } + + /* download data */ + bytecount = ssh_scp_request_get_size(sshc->scp_session); + data->req.maxdownload = bytecount; + Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + data->conn->send_idx = 0; + + myssh_to(data, sshc, SSH_STOP); + return 0; +} + +static int myssh_in_TRANS_INIT(struct Curl_easy *data, struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + int rc = 0; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + if(result) { + sshc->actualcode = result; + myssh_to(data, sshc, SSH_STOP); + return 0; + } + + /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ + ssh_set_blocking(sshc->ssh_session, 1); + + if(data->state.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path); + myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); + } + else { + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path); + myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); + } + + if(!sshc->scp_session) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + return rc; +} + +static void sshc_cleanup(struct ssh_conn *sshc) +{ + if(sshc->initialised) { + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + if(sshc->sftp_session) { + sftp_free(sshc->sftp_session); + sshc->sftp_session = NULL; + } + if(sshc->ssh_session) { + ssh_free(sshc->ssh_session); + sshc->ssh_session = NULL; + } + + /* worst-case scenario cleanup */ + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->scp_session == NULL); + + if(sshc->readdir_tmp) { + ssh_string_free_char(sshc->readdir_tmp); + sshc->readdir_tmp = NULL; + } + if(sshc->quote_attrs) { + sftp_attributes_free(sshc->quote_attrs); + sshc->quote_attrs = NULL; + } + if(sshc->readdir_attrs) { + sftp_attributes_free(sshc->readdir_attrs); + sshc->readdir_attrs = NULL; + } + if(sshc->readdir_link_attrs) { + sftp_attributes_free(sshc->readdir_link_attrs); + sshc->readdir_link_attrs = NULL; + } + if(sshc->privkey) { + ssh_key_free(sshc->privkey); + sshc->privkey = NULL; + } + if(sshc->pubkey) { + ssh_key_free(sshc->pubkey); + sshc->pubkey = NULL; + } + + Curl_safefree(sshc->rsa_pub); + Curl_safefree(sshc->rsa); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + curlx_dyn_free(&sshc->readdir_buf); + Curl_safefree(sshc->readdir_linkPath); + SSH_STRING_FREE_CHAR(sshc->homedir); + sshc->initialised = FALSE; + } +} + /* * ssh_statemach_act() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points @@ -1883,9 +1853,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; int rc = SSH_NO_ERROR, err; - const char *err_msg; *block = FALSE; /* we are not blocking by default */ do { @@ -1942,14 +1910,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, break; case SSH_SFTP_QUOTE_INIT: rc = sshp ? myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + CURLE_FAILED_INIT; break; case SSH_SFTP_POSTQUOTE_INIT: rc = myssh_in_SFTP_POSTQUOTE_INIT(data, sshc); break; case SSH_SFTP_QUOTE: - rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) : CURLE_FAILED_INIT; break; case SSH_SFTP_NEXT_QUOTE: rc = myssh_in_SFTP_NEXT_QUOTE(data, sshc); @@ -1957,23 +1924,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, case SSH_SFTP_QUOTE_STAT: rc = myssh_in_SFTP_QUOTE_STAT(data, sshc); break; - case SSH_SFTP_QUOTE_SETSTAT: rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, sshc->quote_attrs); if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to set SFTP stats failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - /* sshc->actualcode = sftp_error_to_CURLE(err); - * we do not send the actual error; we return - * the error the libssh2 backend is returning */ + myssh_quote_error(data, sshc, "setstat"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -1985,13 +1942,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "symlink command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "symlink"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2003,12 +1954,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "mkdir command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "mkdir"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2020,13 +1966,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "rename command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "rename"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2037,12 +1977,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "rmdir command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "rmdir"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2053,11 +1988,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "rm command failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "rm"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2170,11 +2101,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, case SSH_SFTP_READDIR_INIT: rc = sshp ? myssh_in_SFTP_READDIR_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + CURLE_FAILED_INIT; break; case SSH_SFTP_READDIR: - rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) : CURLE_FAILED_INIT; break; case SSH_SFTP_READDIR_LINK: rc = myssh_in_SFTP_READDIR_LINK(data, sshc); @@ -2187,57 +2117,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, break; case SSH_SFTP_DOWNLOAD_INIT: rc = sshp ? myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + CURLE_FAILED_INIT; break; case SSH_SFTP_DOWNLOAD_STAT: rc = myssh_in_SFTP_DOWNLOAD_STAT(data, sshc); break; case SSH_SFTP_CLOSE: - rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) : CURLE_FAILED_INIT; break; case SSH_SFTP_SHUTDOWN: rc = myssh_in_SFTP_SHUTDOWN(data, sshc); break; - case SSH_SCP_TRANS_INIT: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); - if(result) { - sshc->actualcode = result; - myssh_to(data, sshc, SSH_STOP); - break; - } - - /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ - ssh_set_blocking(sshc->ssh_session, 1); - - if(data->state.upload) { - if(data->state.infilesize < 0) { - failf(data, "SCP requires a known file size for upload"); - sshc->actualcode = CURLE_UPLOAD_FAILED; - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - sshc->scp_session = - ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path); - myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); - } - else { - sshc->scp_session = - ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path); - myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); - } - - if(!sshc->scp_session) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - } + rc = myssh_in_TRANS_INIT(data, sshc, sshp); break; case SSH_SCP_UPLOAD_INIT: @@ -2247,7 +2139,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, } rc = ssh_scp_init(sshc->scp_session); if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); + const char *err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); break; @@ -2258,7 +2150,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, (int)data->set.new_file_perms); if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); + const char *err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); break; @@ -2275,10 +2167,9 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, break; case SSH_SCP_DOWNLOAD_INIT: - rc = ssh_scp_init(sshc->scp_session); if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); + const char *err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); rc = myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); break; @@ -2286,28 +2177,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, myssh_to(data, sshc, SSH_SCP_DOWNLOAD); FALLTHROUGH(); - case SSH_SCP_DOWNLOAD: { - curl_off_t bytecount; - - rc = ssh_scp_pull_request(sshc->scp_session); - if(rc != SSH_SCP_REQUEST_NEWFILE) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND); - break; - } - - /* download data */ - bytecount = ssh_scp_request_get_size(sshc->scp_session); - data->req.maxdownload = (curl_off_t)bytecount; - Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - conn->send_idx = 0; - - myssh_to(data, sshc, SSH_STOP); + case SSH_SCP_DOWNLOAD: + rc = myssh_SSH_SCP_DOWNLOAD(data, sshc); break; - } + case SSH_SCP_DONE: if(data->state.upload) myssh_to(data, sshc, SSH_SCP_SEND_EOF); @@ -2346,41 +2219,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, FALLTHROUGH(); case SSH_SESSION_DISCONNECT: - /* during weird times when we have been prematurely aborted, the channel - is still alive when we reach this state and we MUST kill the channel - properly first */ - if(sshc->scp_session) { - ssh_scp_free(sshc->scp_session); - sshc->scp_session = NULL; - } - - if(sshc->sftp_file) { - sftp_close(sshc->sftp_file); - sshc->sftp_file = NULL; - } - if(sshc->sftp_session) { - sftp_free(sshc->sftp_session); - sshc->sftp_session = NULL; - } - - ssh_disconnect(sshc->ssh_session); - if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { - /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, - tell the connection to forget about it. This libssh - bug is fixed in 0.10.0. */ - conn_forget_socket(data, FIRSTSOCKET); - } - - SSH_STRING_FREE_CHAR(sshc->homedir); - - myssh_to(data, sshc, SSH_SESSION_FREE); + myssh_SESSION_DISCONNECT(data, sshc); FALLTHROUGH(); case SSH_SESSION_FREE: sshc_cleanup(sshc); /* the code we are about to return */ result = sshc->actualcode; memset(sshc, 0, sizeof(struct ssh_conn)); - connclose(conn, "SSH session free"); + connclose(data->conn, "SSH session free"); sshc->state = SSH_SESSION_FREE; /* current */ sshc->nextstate = SSH_NO_STATE; myssh_to(data, sshc, SSH_STOP); @@ -2405,7 +2251,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(!result && (sshc->state == SSH_STOP)) result = sshc->actualcode; CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - myssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), result, *block); return result; } @@ -2439,22 +2285,6 @@ static CURLcode myssh_pollset(struct Curl_easy *data, return CURLE_OK; } -static void myssh_block2waitfor(struct connectdata *conn, - struct ssh_conn *sshc, - bool block) -{ - (void)conn; - if(block) { - int dir = ssh_get_poll_flags(sshc->ssh_session); - /* translate the libssh define bits into our own bit defines */ - sshc->waitfor = - ((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) | - ((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0); - } - else - sshc->waitfor = 0; -} - /* called repeatedly until done from multi.c */ static CURLcode myssh_multi_statemach(struct Curl_easy *data, bool *done) @@ -2496,7 +2326,7 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data, if(result) break; - left_ms = Curl_timeleft_ms(data, FALSE); + left_ms = Curl_timeleft_ms(data); if(left_ms < 0) { failf(data, "Operation timed out"); return CURLE_OPERATION_TIMEDOUT; @@ -2577,7 +2407,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; CURL_TRC_SSH(data, "myssh_connect"); - if(conn->handler->protocol & CURLPROTO_SCP) { + if(conn->scheme->protocol & CURLPROTO_SCP) { conn->recv[FIRSTSOCKET] = scp_recv; conn->send[FIRSTSOCKET] = scp_send; } @@ -2730,89 +2560,6 @@ static CURLcode scp_perform(struct Curl_easy *data, return result; } -static CURLcode myssh_do_it(struct Curl_easy *data, bool *done) -{ - CURLcode result; - bool connected = FALSE; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - - *done = FALSE; /* default to false */ - if(!sshc) - return CURLE_FAILED_INIT; - - data->req.size = -1; /* make sure this is unknown at this point */ - - sshc->actualcode = CURLE_OK; /* reset error code */ - sshc->secondCreateDirs = 0; /* reset the create directory attempt state - variable */ - - Curl_pgrsReset(data); - - if(conn->handler->protocol & CURLPROTO_SCP) - result = scp_perform(data, &connected, done); - else - result = sftp_perform(data, &connected, done); - - return result; -} - -static void sshc_cleanup(struct ssh_conn *sshc) -{ - if(sshc->initialised) { - if(sshc->sftp_file) { - sftp_close(sshc->sftp_file); - sshc->sftp_file = NULL; - } - if(sshc->sftp_session) { - sftp_free(sshc->sftp_session); - sshc->sftp_session = NULL; - } - if(sshc->ssh_session) { - ssh_free(sshc->ssh_session); - sshc->ssh_session = NULL; - } - - /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->scp_session == NULL); - - if(sshc->readdir_tmp) { - ssh_string_free_char(sshc->readdir_tmp); - sshc->readdir_tmp = NULL; - } - if(sshc->quote_attrs) { - sftp_attributes_free(sshc->quote_attrs); - sshc->quote_attrs = NULL; - } - if(sshc->readdir_attrs) { - sftp_attributes_free(sshc->readdir_attrs); - sshc->readdir_attrs = NULL; - } - if(sshc->readdir_link_attrs) { - sftp_attributes_free(sshc->readdir_link_attrs); - sshc->readdir_link_attrs = NULL; - } - if(sshc->privkey) { - ssh_key_free(sshc->privkey); - sshc->privkey = NULL; - } - if(sshc->pubkey) { - ssh_key_free(sshc->pubkey); - sshc->pubkey = NULL; - } - - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - curlx_dyn_free(&sshc->readdir_buf); - Curl_safefree(sshc->readdir_linkPath); - SSH_STRING_FREE_CHAR(sshc->homedir); - sshc->initialised = FALSE; - } -} - /* BLOCKING, but the function is using the state machine so the only reason this is still blocking is that the multi interface code has no support for disconnecting operations that takes a while */ @@ -3174,6 +2921,33 @@ static CURLcode sftp_recv(struct Curl_easy *data, int sockindex, } } +static CURLcode myssh_do_it(struct Curl_easy *data, bool *done) +{ + CURLcode result; + bool connected = FALSE; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + + *done = FALSE; /* default to false */ + if(!sshc) + return CURLE_FAILED_INIT; + + data->req.size = -1; /* make sure this is unknown at this point */ + + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs = 0; /* reset the create directory attempt state + variable */ + + Curl_pgrsReset(data); + + if(conn->scheme->protocol & CURLPROTO_SCP) + result = scp_perform(data, &connected, done); + else + result = sftp_perform(data, &connected, done); + + return result; +} + CURLcode Curl_ssh_init(void) { if(ssh_init()) { @@ -3193,4 +2967,50 @@ void Curl_ssh_version(char *buffer, size_t buflen) (void)curl_msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); } +/* + * SCP. + */ +const struct Curl_protocol Curl_protocol_scp = { + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + myssh_pollset, /* proto_pollset */ + myssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + myssh_pollset, /* perform_pollset */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +/* + * SFTP. + */ +const struct Curl_protocol Curl_protocol_sftp = { + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + sftp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + sftp_doing, /* doing */ + myssh_pollset, /* proto_pollset */ + myssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + myssh_pollset, /* perform_pollset */ + sftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* USE_LIBSSH */ diff --git a/vendor/curl/lib/vssh/libssh2.c b/vendor/curl/lib/vssh/libssh2.c index 5b2b337..63f5735 100644 --- a/vendor/curl/lib/vssh/libssh2.c +++ b/vendor/curl/lib/vssh/libssh2.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_LIBSSH2 @@ -41,110 +41,89 @@ #include #endif -#include "../urldata.h" -#include "../sendf.h" -#include "../curl_trc.h" -#include "../hostip.h" -#include "../progress.h" -#include "../transfer.h" -#include "ssh.h" -#include "../url.h" -#include "../cfilters.h" -#include "../connect.h" -#include "../parsedate.h" /* for the week day and month names */ -#include "../multiif.h" -#include "../select.h" -#include "../curlx/fopen.h" -#include "vssh.h" -#include "../curlx/strparse.h" -#include "../curlx/base64.h" /* for base64 encoding/decoding */ - -/* Local functions: */ -static const char *sftp_libssh2_strerror(unsigned long err); -static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); -static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); -static LIBSSH2_FREE_FUNC(my_libssh2_free); -static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, - struct ssh_conn *sshc); -static CURLcode ssh_connect(struct Curl_easy *data, bool *done); -static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode ssh_do(struct Curl_easy *data, bool *done); -static CURLcode scp_done(struct Curl_easy *data, CURLcode c, bool premature); -static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode scp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection); -static CURLcode sftp_done(struct Curl_easy *data, CURLcode, bool premature); -static CURLcode sftp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode sftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode sftp_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done); -static CURLcode ssh_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode ssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static void ssh_attach(struct Curl_easy *data, struct connectdata *conn); -static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, - bool block); -/* - * SCP protocol handler. - */ +#include "urldata.h" +#include "sendf.h" +#include "curl_trc.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "vssh/ssh.h" +#include "url.h" +#include "cfilters.h" +#include "connect.h" +#include "parsedate.h" /* for the week day and month names */ +#include "multiif.h" +#include "select.h" +#include "curlx/fopen.h" +#include "vssh/vssh.h" +#include "curlx/strparse.h" +#include "curlx/base64.h" /* for base64 encoding/decoding */ -const struct Curl_handler Curl_handler_scp = { - "SCP", /* scheme */ - ssh_setup_connection, /* setup_connection */ - ssh_do, /* do_it */ - scp_done, /* done */ - ZERO_NULL, /* do_more */ - ssh_connect, /* connect_it */ - ssh_multi_statemach, /* connecting */ - scp_doing, /* doing */ - ssh_pollset, /* proto_pollset */ - ssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ssh_pollset, /* perform_pollset */ - scp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ssh_attach, /* attach */ - ZERO_NULL, /* follow */ - PORT_SSH, /* defport */ - CURLPROTO_SCP, /* protocol */ - CURLPROTO_SCP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE -}; +static const char *sftp_libssh2_strerror(unsigned long err) +{ + switch(err) { + case LIBSSH2_FX_NO_SUCH_FILE: + return "No such file or directory"; -/* - * SFTP protocol handler. - */ + case LIBSSH2_FX_PERMISSION_DENIED: + return "Permission denied"; -const struct Curl_handler Curl_handler_sftp = { - "SFTP", /* scheme */ - ssh_setup_connection, /* setup_connection */ - ssh_do, /* do_it */ - sftp_done, /* done */ - ZERO_NULL, /* do_more */ - ssh_connect, /* connect_it */ - ssh_multi_statemach, /* connecting */ - sftp_doing, /* doing */ - ssh_pollset, /* proto_pollset */ - ssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ssh_pollset, /* perform_pollset */ - sftp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ssh_attach, /* attach */ - ZERO_NULL, /* follow */ - PORT_SSH, /* defport */ - CURLPROTO_SFTP, /* protocol */ - CURLPROTO_SFTP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE -}; + case LIBSSH2_FX_FAILURE: + return "Operation failed"; + + case LIBSSH2_FX_BAD_MESSAGE: + return "Bad message from SFTP server"; + + case LIBSSH2_FX_NO_CONNECTION: + return "Not connected to SFTP server"; + + case LIBSSH2_FX_CONNECTION_LOST: + return "Connection to SFTP server lost"; + + case LIBSSH2_FX_OP_UNSUPPORTED: + return "Operation not supported by SFTP server"; + + case LIBSSH2_FX_INVALID_HANDLE: + return "Invalid handle"; + + case LIBSSH2_FX_NO_SUCH_PATH: + return "No such file or directory"; + + case LIBSSH2_FX_FILE_ALREADY_EXISTS: + return "File already exists"; + + case LIBSSH2_FX_WRITE_PROTECT: + return "File is write protected"; + + case LIBSSH2_FX_NO_MEDIA: + return "No media"; + + case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: + return "Disk full"; + + case LIBSSH2_FX_QUOTA_EXCEEDED: + return "User quota exceeded"; + + case LIBSSH2_FX_UNKNOWN_PRINCIPLE: + return "Unknown principle"; + + case LIBSSH2_FX_LOCK_CONFlICT: + return "File lock conflict"; + + case LIBSSH2_FX_DIR_NOT_EMPTY: + return "Directory not empty"; + + case LIBSSH2_FX_NOT_A_DIRECTORY: + return "Not a directory"; + + case LIBSSH2_FX_INVALID_FILENAME: + return "Invalid filename"; + + case LIBSSH2_FX_LINK_LOOP: + return "Link points to itself"; + } + return "Unknown error in libssh2"; +} static void kbd_callback(const char *name, int name_len, const char *instruction, int instruction_len, @@ -272,101 +251,6 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free) Curl_cfree(ptr); } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static const char *myssh_statename(sshstate state) -{ - static const char * const names[] = { - "SSH_STOP", - "SSH_INIT", - "SSH_S_STARTUP", - "SSH_HOSTKEY", - "SSH_AUTHLIST", - "SSH_AUTH_PKEY_INIT", - "SSH_AUTH_PKEY", - "SSH_AUTH_PASS_INIT", - "SSH_AUTH_PASS", - "SSH_AUTH_AGENT_INIT", - "SSH_AUTH_AGENT_LIST", - "SSH_AUTH_AGENT", - "SSH_AUTH_HOST_INIT", - "SSH_AUTH_HOST", - "SSH_AUTH_KEY_INIT", - "SSH_AUTH_KEY", - "SSH_AUTH_GSSAPI", - "SSH_AUTH_DONE", - "SSH_SFTP_INIT", - "SSH_SFTP_REALPATH", - "SSH_SFTP_QUOTE_INIT", - "SSH_SFTP_POSTQUOTE_INIT", - "SSH_SFTP_QUOTE", - "SSH_SFTP_NEXT_QUOTE", - "SSH_SFTP_QUOTE_STAT", - "SSH_SFTP_QUOTE_SETSTAT", - "SSH_SFTP_QUOTE_SYMLINK", - "SSH_SFTP_QUOTE_MKDIR", - "SSH_SFTP_QUOTE_RENAME", - "SSH_SFTP_QUOTE_RMDIR", - "SSH_SFTP_QUOTE_UNLINK", - "SSH_SFTP_QUOTE_STATVFS", - "SSH_SFTP_GETINFO", - "SSH_SFTP_FILETIME", - "SSH_SFTP_TRANS_INIT", - "SSH_SFTP_UPLOAD_INIT", - "SSH_SFTP_CREATE_DIRS_INIT", - "SSH_SFTP_CREATE_DIRS", - "SSH_SFTP_CREATE_DIRS_MKDIR", - "SSH_SFTP_READDIR_INIT", - "SSH_SFTP_READDIR", - "SSH_SFTP_READDIR_LINK", - "SSH_SFTP_READDIR_BOTTOM", - "SSH_SFTP_READDIR_DONE", - "SSH_SFTP_DOWNLOAD_INIT", - "SSH_SFTP_DOWNLOAD_STAT", - "SSH_SFTP_CLOSE", - "SSH_SFTP_SHUTDOWN", - "SSH_SCP_TRANS_INIT", - "SSH_SCP_UPLOAD_INIT", - "SSH_SCP_DOWNLOAD_INIT", - "SSH_SCP_DOWNLOAD", - "SSH_SCP_DONE", - "SSH_SCP_SEND_EOF", - "SSH_SCP_WAIT_EOF", - "SSH_SCP_WAIT_CLOSE", - "SSH_SCP_CHANNEL_FREE", - "SSH_SESSION_DISCONNECT", - "SSH_SESSION_FREE", - "QUIT" - }; - /* a precaution to make sure the lists are in sync */ - DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST); - return ((size_t)state < CURL_ARRAYSIZE(names)) ? names[state] : ""; -} -#else -#define myssh_statename(x) "" -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ - -#define myssh_state(x, y, z) myssh_set_state(x, y, z) - -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void myssh_set_state(struct Curl_easy *data, - struct ssh_conn *sshc, - sshstate nowstate) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(sshc->state != nowstate) { - CURL_TRC_SSH(data, "[%s] -> [%s]", - myssh_statename(sshc->state), - myssh_statename(nowstate)); - } -#else - (void)data; -#endif - sshc->state = nowstate; -} - static int sshkeycallback(CURL *easy, const struct curl_khkey *knownkey, /* known */ const struct curl_khkey *foundkey, /* found */ @@ -523,7 +407,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, switch(rc) { default: /* unknown return codes will equal reject */ case CURLKHSTAT_REJECT: - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); FALLTHROUGH(); case CURLKHSTAT_DEFER: /* DEFER means bail out but keep the SSH_HOSTKEY state */ @@ -594,7 +478,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, failf(data, "Denied establishing ssh session: sha256 fingerprint " "not available"); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } @@ -602,13 +486,13 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, * See libssh2_hostkey_hash documentation. */ if(curlx_base64_encode((const uint8_t *)fingerprint, 32, &fingerprint_b64, &fingerprint_b64_len) != CURLE_OK) { - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } if(!fingerprint_b64) { failf(data, "sha256 fingerprint could not be encoded"); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } @@ -634,7 +518,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, "Denied establishing ssh session: mismatch sha256 fingerprint. " "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); curlx_free(fingerprint_b64); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } @@ -674,7 +558,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, "Denied establishing ssh session: md5 fingerprint " "not available"); } - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } infof(data, "MD5 checksum match"); @@ -695,12 +579,12 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, (int)keytype, remotekey, keylen); Curl_set_in_callback(data, FALSE); if(rc != CURLKHMATCH_OK) { - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } } else { - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } return CURLE_OK; @@ -815,10 +699,10 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, rc = libssh2_session_method_pref(sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method); if(rc) { - char *errmsg = NULL; + char *err_msg = NULL; int errlen; - libssh2_session_last_error(sshc->ssh_session, &errmsg, &errlen, 0); - failf(data, "libssh2 method '%s' failed: %s", hostkey_method, errmsg); + libssh2_session_last_error(sshc->ssh_session, &err_msg, &errlen, 0); + failf(data, "libssh2 method '%s' failed: %s", hostkey_method, err_msg); result = libssh2_session_error_to_CURLE(rc); } } @@ -853,7 +737,7 @@ static CURLcode sftp_quote(struct Curl_easy *data, * 'sshc->quote_item' is already verified to be non-NULL before it * switched to this state. */ - char *cmd = sshc->quote_item->data; + const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never @@ -881,7 +765,7 @@ static CURLcode sftp_quote(struct Curl_easy *data, result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); curlx_free(tmp); if(!result) - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return result; } @@ -931,7 +815,7 @@ static CURLcode sftp_quote(struct Curl_easy *data, return_quote_error(data, sshc); memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT); + myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT); return result; } if(!strncmp(cmd, "ln ", 3) || @@ -948,14 +832,14 @@ static CURLcode sftp_quote(struct Curl_easy *data, } if(*cp) return_quote_error(data, sshc); - myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK); + myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK); return result; } else if(!strncmp(cmd, "mkdir ", 6)) { if(*cp) return_quote_error(data, sshc); /* create directory */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR); + myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR); return result; } else if(!strncmp(cmd, "rename ", 7)) { @@ -971,26 +855,26 @@ static CURLcode sftp_quote(struct Curl_easy *data, } if(*cp) return_quote_error(data, sshc); - myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME); + myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME); return result; } else if(!strncmp(cmd, "rmdir ", 6)) { if(*cp) return_quote_error(data, sshc); /* delete directory */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR); + myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR); return result; } else if(!strncmp(cmd, "rm ", 3)) { if(*cp) return_quote_error(data, sshc); - myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK); + myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK); return result; } else if(!strncmp(cmd, "statvfs ", 8)) { if(*cp) return_quote_error(data, sshc); - myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS); + myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS); return result; } @@ -1078,22 +962,22 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */ if(sshc->secondCreateDirs) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); failf(data, "Creating the dir/file failed: %s", sftp_libssh2_strerror(sftperr)); return sftp_libssh2_error_to_CURLE(sftperr); } - if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) || - (sftperr == LIBSSH2_FX_FAILURE) || - (sftperr == LIBSSH2_FX_NO_SUCH_PATH)) && - (data->set.ftp_create_missing_dirs && - (strlen(sshp->path) > 1))) { + if((sftperr == LIBSSH2_FX_NO_SUCH_FILE || + sftperr == LIBSSH2_FX_FAILURE || + sftperr == LIBSSH2_FX_NO_SUCH_PATH) && + data->set.ftp_create_missing_dirs && + (strlen(sshp->path) > 1)) { /* try to create the path remotely */ sshc->secondCreateDirs = 1; - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_INIT); return CURLE_OK; } - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); result = sftp_libssh2_error_to_CURLE(sftperr); if(!result) { /* Sometimes, for some reason libssh2_sftp_last_error() returns zero @@ -1177,7 +1061,7 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, state machine to move on as soon as possible so mark this as dirty */ Curl_multi_mark_dirty(data); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1205,7 +1089,7 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, /* To ponder about: should really the lib be messing about with the HOME environment variable etc? */ char *home = curl_getenv("HOME"); - struct_stat sbuf; + curlx_struct_stat sbuf; /* If no private key file is specified, try some common paths. */ if(home) { @@ -1243,11 +1127,11 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, /* * Unless the user explicitly specifies a public key file, let * libssh2 extract the public key from the private key file. - * This is done by simply passing sshc->rsa_pub = NULL. + * This is done by passing sshc->rsa_pub = NULL. */ - if(!out_of_memory && data->set.str[STRING_SSH_PUBLIC_KEY] + if(!out_of_memory && data->set.str[STRING_SSH_PUBLIC_KEY] && /* treat empty string the same way as NULL */ - && data->set.str[STRING_SSH_PUBLIC_KEY][0]) { + data->set.str[STRING_SSH_PUBLIC_KEY][0]) { sshc->rsa_pub = curlx_strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); if(!sshc->rsa_pub) out_of_memory = TRUE; @@ -1256,7 +1140,7 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, if(out_of_memory || !sshc->rsa) { Curl_safefree(sshc->rsa); Curl_safefree(sshc->rsa_pub); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_OUT_OF_MEMORY; } @@ -1268,10 +1152,10 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, infof(data, "Using SSH public key file '%s'", sshc->rsa_pub); infof(data, "Using SSH private key file '%s'", sshc->rsa); - myssh_state(data, sshc, SSH_AUTH_PKEY); + myssh_to(data, sshc, SSH_AUTH_PKEY); } else { - myssh_state(data, sshc, SSH_AUTH_PASS_INIT); + myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } return 0; } @@ -1281,7 +1165,7 @@ static CURLcode sftp_quote_stat(struct Curl_easy *data, struct SSHPROTO *sshp, bool *blockp) { - char *cmd = sshc->quote_item->data; + const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never @@ -1379,7 +1263,7 @@ static CURLcode sftp_quote_stat(struct Curl_easy *data, } /* Now send the completed structure... */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT); + myssh_to(data, sshc, SSH_SFTP_QUOTE_SETSTAT); return CURLE_OK; fail: Curl_safefree(sshc->quote_path1); @@ -1405,7 +1289,7 @@ static CURLcode sftp_download_stat(struct Curl_easy *data, (attrs.filesize == 0)) { /* * libssh2_sftp_open() did not return an error, so maybe the server - * just does not support stat() + * does not support stat() * OR the server does not return a file size with a stat() * OR file size is 0 */ @@ -1469,7 +1353,7 @@ static CURLcode sftp_download_stat(struct Curl_easy *data, /* no data to transfer */ Curl_xfer_setup_nop(data); infof(data, "File already completely downloaded"); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size); @@ -1477,7 +1361,7 @@ static CURLcode sftp_download_stat(struct Curl_easy *data, /* not set by Curl_xfer_setup to preserve keepon bits */ data->conn->send_idx = 0; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1518,17 +1402,17 @@ static CURLcode sftp_readdir(struct Curl_easy *data, LIBSSH2_SFTP_S_IFLNK)) { result = curlx_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path, sshp->readdir_filename); - myssh_state(data, sshc, SSH_SFTP_READDIR_LINK); + myssh_to(data, sshc, SSH_SFTP_READDIR_LINK); } else { - myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM); + myssh_to(data, sshc, SSH_SFTP_READDIR_BOTTOM); } } return result; } } else if(!rc) { - myssh_state(data, sshc, SSH_SFTP_READDIR_DONE); + myssh_to(data, sshc, SSH_SFTP_READDIR_DONE); } else { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); @@ -1536,7 +1420,7 @@ static CURLcode sftp_readdir(struct Curl_easy *data, failf(data, "Could not open remote file for reading: %s :: %d", sftp_libssh2_strerror(sftperr), libssh2_session_last_errno(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); } return result; } @@ -1554,9 +1438,9 @@ static CURLcode ssh_state_init(struct Curl_easy *data, result = ssh_force_knownhost_key_type(data, sshc); if(result) - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); else - myssh_state(data, sshc, SSH_S_STARTUP); + myssh_to(data, sshc, SSH_S_STARTUP); return result; } @@ -1574,11 +1458,11 @@ static CURLcode ssh_state_startup(struct Curl_easy *data, (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_FAILED_INIT; } - myssh_state(data, sshc, SSH_HOSTKEY); + myssh_to(data, sshc, SSH_HOSTKEY); return CURLE_OK; } @@ -1592,7 +1476,7 @@ static CURLcode ssh_state_hostkey(struct Curl_easy *data, */ CURLcode result = ssh_check_fingerprint(data, sshc); if(!result) - myssh_state(data, sshc, SSH_AUTHLIST); + myssh_to(data, sshc, SSH_AUTHLIST); return result; } @@ -1619,19 +1503,19 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, if(libssh2_userauth_authenticated(sshc->ssh_session)) { sshc->authed = TRUE; infof(data, "SSH user accepted with no authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } rc = libssh2_session_last_errno(sshc->ssh_session); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return libssh2_session_error_to_CURLE(rc); } infof(data, "SSH authentication methods available: %s", sshc->authlist); - myssh_state(data, sshc, SSH_AUTH_PKEY_INIT); + myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); return CURLE_OK; } @@ -1657,7 +1541,7 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, if(rc == 0) { sshc->authed = TRUE; infof(data, "Initialized SSH public key authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); } else { char *err_msg = NULL; @@ -1671,7 +1555,7 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); } infof(data, "SSH public key authentication failed: %s", err_msg); - myssh_state(data, sshc, SSH_AUTH_PASS_INIT); + myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } return CURLE_OK; } @@ -1681,10 +1565,10 @@ static CURLcode ssh_state_auth_pass_init(struct Curl_easy *data, { if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) && (strstr(sshc->authlist, "password") != NULL)) { - myssh_state(data, sshc, SSH_AUTH_PASS); + myssh_to(data, sshc, SSH_AUTH_PASS); } else { - myssh_state(data, sshc, SSH_AUTH_HOST_INIT); + myssh_to(data, sshc, SSH_AUTH_HOST_INIT); } return CURLE_OK; } @@ -1705,10 +1589,10 @@ static CURLcode ssh_state_auth_pass(struct Curl_easy *data, if(rc == 0) { sshc->authed = TRUE; infof(data, "Initialized password authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); } else { - myssh_state(data, sshc, SSH_AUTH_HOST_INIT); + myssh_to(data, sshc, SSH_AUTH_HOST_INIT); } return CURLE_OK; } @@ -1718,10 +1602,10 @@ static CURLcode ssh_state_auth_host_init(struct Curl_easy *data, { if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) && (strstr(sshc->authlist, "hostbased") != NULL)) { - myssh_state(data, sshc, SSH_AUTH_HOST); + myssh_to(data, sshc, SSH_AUTH_HOST); } else { - myssh_state(data, sshc, SSH_AUTH_AGENT_INIT); + myssh_to(data, sshc, SSH_AUTH_AGENT_INIT); } return CURLE_OK; } @@ -1741,7 +1625,7 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, if(!sshc->ssh_agent) { infof(data, "Could not create agent object"); - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); return CURLE_OK; } } @@ -1751,14 +1635,14 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, return CURLE_AGAIN; if(rc < 0) { infof(data, "Failure connecting to agent"); - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { - myssh_state(data, sshc, SSH_AUTH_AGENT_LIST); + myssh_to(data, sshc, SSH_AUTH_AGENT_LIST); } } else - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); return CURLE_OK; } @@ -1771,10 +1655,10 @@ static CURLcode ssh_state_auth_agent_list(struct Curl_easy *data, return CURLE_AGAIN; if(rc < 0) { infof(data, "Failure requesting identities to agent"); - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { - myssh_state(data, sshc, SSH_AUTH_AGENT); + myssh_to(data, sshc, SSH_AUTH_AGENT); sshc->sshagent_prev_identity = NULL; } return CURLE_OK; @@ -1815,10 +1699,10 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_NONE) { sshc->authed = TRUE; infof(data, "Agent based authentication successful"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); } else { - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } return CURLE_OK; } @@ -1828,10 +1712,10 @@ static CURLcode ssh_state_auth_key_init(struct Curl_easy *data, { if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) { - myssh_state(data, sshc, SSH_AUTH_KEY); + myssh_to(data, sshc, SSH_AUTH_KEY); } else { - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); } return CURLE_OK; } @@ -1853,7 +1737,7 @@ static CURLcode ssh_state_auth_key(struct Curl_easy *data, if(rc == 0) { sshc->authed = TRUE; infof(data, "Initialized keyboard interactive authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } return CURLE_LOGIN_DENIED; @@ -1865,7 +1749,7 @@ static CURLcode ssh_state_auth_done(struct Curl_easy *data, struct connectdata *conn = data->conn; if(!sshc->authed) { failf(data, "Authentication failure"); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_LOGIN_DENIED; } @@ -1879,12 +1763,12 @@ static CURLcode ssh_state_auth_done(struct Curl_easy *data, data->conn->recv_idx = FIRSTSOCKET; conn->send_idx = -1; - if(conn->handler->protocol == CURLPROTO_SFTP) { - myssh_state(data, sshc, SSH_SFTP_INIT); + if(conn->scheme->protocol == CURLPROTO_SFTP) { + myssh_to(data, sshc, SSH_SFTP_INIT); return CURLE_OK; } infof(data, "SSH CONNECT phase done"); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1903,10 +1787,10 @@ static CURLcode ssh_state_sftp_init(struct Curl_easy *data, (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); failf(data, "Failure initializing sftp session: %s", err_msg); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_FAILED_INIT; } - myssh_state(data, sshc, SSH_SFTP_REALPATH); + myssh_to(data, sshc, SSH_SFTP_REALPATH); return CURLE_OK; } @@ -1924,7 +1808,7 @@ static CURLcode ssh_state_sftp_realpath(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); if(rc > 0) { curlx_free(sshc->homedir); sshc->homedir = curlx_strdup(sshp->readdir_filename); @@ -1963,17 +1847,17 @@ static CURLcode ssh_state_sftp_quote_init(struct Curl_easy *data, { CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); if(result) { - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return result; } if(data->set.quote) { infof(data, "Sending quote commands"); sshc->quote_item = data->set.quote; - myssh_state(data, sshc, SSH_SFTP_QUOTE); + myssh_to(data, sshc, SSH_SFTP_QUOTE); } else { - myssh_state(data, sshc, SSH_SFTP_GETINFO); + myssh_to(data, sshc, SSH_SFTP_GETINFO); } return CURLE_OK; } @@ -1984,10 +1868,10 @@ static CURLcode ssh_state_sftp_postquote_init(struct Curl_easy *data, if(data->set.postquote) { infof(data, "Sending quote commands"); sshc->quote_item = data->set.postquote; - myssh_state(data, sshc, SSH_SFTP_QUOTE); + myssh_to(data, sshc, SSH_SFTP_QUOTE); } else { - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); } return CURLE_OK; } @@ -1999,7 +1883,7 @@ static CURLcode ssh_state_sftp_quote(struct Curl_easy *data, /* Send quote commands */ CURLcode result = sftp_quote(data, sshc, sshp); if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; } return result; @@ -2014,15 +1898,15 @@ static CURLcode ssh_state_sftp_next_quote(struct Curl_easy *data, sshc->quote_item = sshc->quote_item->next; if(sshc->quote_item) { - myssh_state(data, sshc, SSH_SFTP_QUOTE); + myssh_to(data, sshc, SSH_SFTP_QUOTE); } else { if(sshc->nextstate != SSH_NO_STATE) { - myssh_state(data, sshc, sshc->nextstate); + myssh_to(data, sshc, sshc->nextstate); sshc->nextstate = SSH_NO_STATE; } else { - myssh_state(data, sshc, SSH_SFTP_GETINFO); + myssh_to(data, sshc, SSH_SFTP_GETINFO); } } return CURLE_OK; @@ -2035,7 +1919,7 @@ static CURLcode ssh_state_sftp_quote_stat(struct Curl_easy *data, { CURLcode result = sftp_quote_stat(data, sshc, sshp, blockp); if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; } return result; @@ -2059,11 +1943,11 @@ static CURLcode ssh_state_sftp_quote_setstat(struct Curl_easy *data, sshc->quote_path2, sftp_libssh2_strerror(sftperr)); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2086,11 +1970,11 @@ static CURLcode ssh_state_sftp_quote_symlink(struct Curl_easy *data, sftp_libssh2_strerror(sftperr)); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2108,11 +1992,11 @@ static CURLcode ssh_state_sftp_quote_mkdir(struct Curl_easy *data, failf(data, "mkdir \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2138,11 +2022,11 @@ static CURLcode ssh_state_sftp_quote_rename(struct Curl_easy *data, sftp_libssh2_strerror(sftperr)); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2159,11 +2043,11 @@ static CURLcode ssh_state_sftp_quote_rmdir(struct Curl_easy *data, failf(data, "rmdir \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2180,11 +2064,11 @@ static CURLcode ssh_state_sftp_quote_unlink(struct Curl_easy *data, failf(data, "rm \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2204,7 +2088,7 @@ static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data, failf(data, "statvfs \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } @@ -2234,7 +2118,7 @@ static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data, statvfs.f_fsid, statvfs.f_flag, statvfs.f_namemax); if(!tmp) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_OUT_OF_MEMORY; } @@ -2242,12 +2126,12 @@ static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data, result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); curlx_free(tmp); if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return result; } } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return CURLE_OK; } @@ -2274,11 +2158,11 @@ static CURLcode ssh_state_sftp_create_dirs_mkdir(struct Curl_easy *data, if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) && (sftperr != LIBSSH2_FX_FAILURE) && (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); return sftp_libssh2_error_to_CURLE(sftperr); } } - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); return CURLE_OK; } @@ -2288,7 +2172,7 @@ static CURLcode ssh_state_sftp_readdir_init(struct Curl_easy *data, { Curl_pgrsSetDownloadSize(data, -1); if(data->req.no_body) { - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2308,10 +2192,10 @@ static CURLcode ssh_state_sftp_readdir_init(struct Curl_easy *data, sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "Could not open directory for reading: %s", sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); return sftp_libssh2_error_to_CURLE(sftperr); } - myssh_state(data, sshc, SSH_SFTP_READDIR); + myssh_to(data, sshc, SSH_SFTP_READDIR); return CURLE_OK; } @@ -2338,9 +2222,9 @@ static CURLcode ssh_state_sftp_readdir_link(struct Curl_easy *data, /* append filename and extra output */ result = curlx_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename); if(result) - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); else - myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM); + myssh_to(data, sshc, SSH_SFTP_READDIR_BOTTOM); return result; } @@ -2373,7 +2257,7 @@ static CURLcode ssh_state_scp_download_init(struct Curl_easy *data, ssh_err = libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); failf(data, "%s", err_msg); - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); return libssh2_session_error_to_CURLE(ssh_err); } @@ -2385,7 +2269,7 @@ static CURLcode ssh_state_scp_download_init(struct Curl_easy *data, /* not set by Curl_xfer_setup to preserve keepon bits */ data->conn->send_idx = 0; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2413,14 +2297,14 @@ static CURLcode ssh_state_sftp_close(struct Curl_easy *data, /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT After nextstate is executed, the control should come back to - SSH_SFTP_CLOSE to pass the correct result back */ + SSH_SFTP_CLOSE to pass the correct result back */ if(sshc->nextstate != SSH_NO_STATE && sshc->nextstate != SSH_SFTP_CLOSE) { - myssh_state(data, sshc, sshc->nextstate); + myssh_to(data, sshc, sshc->nextstate); sshc->nextstate = SSH_SFTP_CLOSE; } else - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2457,7 +2341,7 @@ static CURLcode ssh_state_sftp_shutdown(struct Curl_easy *data, Curl_safefree(sshc->homedir); - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); return CURLE_OK; } @@ -2482,10 +2366,10 @@ static CURLcode ssh_state_sftp_download_init(struct Curl_easy *data, sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "Could not open remote file for reading: %s", sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); return sftp_libssh2_error_to_CURLE(sftperr); } - myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT); + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_STAT); return CURLE_OK; } @@ -2513,7 +2397,7 @@ static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data, ssh_err = libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); failf(data, "%s", err_msg); - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); result = libssh2_session_error_to_CURLE(ssh_err); /* Map generic errors to upload failed */ @@ -2531,7 +2415,7 @@ static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data, /* not set by Curl_xfer_setup to preserve keepon bits */ data->conn->recv_idx = FIRSTSOCKET; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2570,16 +2454,115 @@ static CURLcode ssh_state_session_disconnect(struct Curl_easy *data, Curl_safefree(sshc->homedir); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_OK; } + +static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, + bool block) +{ + int rc; + + if(sshc->kh) { + libssh2_knownhost_free(sshc->kh); + sshc->kh = NULL; + } + + if(sshc->ssh_agent) { + rc = libssh2_agent_disconnect(sshc->ssh_agent); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to disconnect from libssh2 agent: %d %s", + rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + libssh2_agent_free(sshc->ssh_agent); + sshc->ssh_agent = NULL; + + /* NB: there is no need to free identities, they are part of internal + agent stuff */ + sshc->sshagent_identity = NULL; + sshc->sshagent_prev_identity = NULL; + } + + if(sshc->sftp_handle) { + rc = libssh2_sftp_close(sshc->sftp_handle); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + sshc->sftp_handle = NULL; + } + + if(sshc->ssh_channel) { + rc = libssh2_channel_free(sshc->ssh_channel); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 scp subsystem: %d %s", rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + sshc->ssh_channel = NULL; + } + + if(sshc->sftp_session) { + rc = libssh2_sftp_shutdown(sshc->sftp_session); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to stop libssh2 sftp subsystem: %d %s", rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + sshc->sftp_session = NULL; + } + + if(sshc->ssh_session) { + rc = libssh2_session_free(sshc->ssh_session); + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg); + } + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + sshc->ssh_session = NULL; + } + + /* worst-case scenario cleanup */ + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->ssh_channel == NULL); + DEBUGASSERT(sshc->sftp_session == NULL); + DEBUGASSERT(sshc->sftp_handle == NULL); + DEBUGASSERT(sshc->kh == NULL); + DEBUGASSERT(sshc->ssh_agent == NULL); + + Curl_safefree(sshc->rsa_pub); + Curl_safefree(sshc->rsa); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + Curl_safefree(sshc->homedir); + + return CURLE_OK; +} + /* * ssh_statemachine() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN * meaning it wants to be called again when the socket is ready */ - static CURLcode ssh_statemachine(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp, @@ -2632,7 +2615,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_AUTH_HOST: - myssh_state(data, sshc, SSH_AUTH_AGENT_INIT); + myssh_to(data, sshc, SSH_AUTH_AGENT_INIT); break; case SSH_AUTH_AGENT_INIT: @@ -2722,10 +2705,10 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, case SSH_SFTP_GETINFO: if(data->set.get_filetime) { - myssh_state(data, sshc, SSH_SFTP_FILETIME); + myssh_to(data, sshc, SSH_SFTP_FILETIME); } else { - myssh_state(data, sshc, SSH_SFTP_TRANS_INIT); + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); } break; @@ -2748,18 +2731,18 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, data->info.filetime = (time_t)attrs.mtime; } - myssh_state(data, sshc, SSH_SFTP_TRANS_INIT); + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); break; } case SSH_SFTP_TRANS_INIT: if(data->state.upload) - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); else if(sshp) { if(sshp->path[strlen(sshp->path) - 1] == '/') - myssh_state(data, sshc, SSH_SFTP_READDIR_INIT); + myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); else - myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT); + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); } else result = CURLE_FAILED_INIT; @@ -2769,7 +2752,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = sshp ? sftp_upload_init(data, sshc, sshp, block) : CURLE_FAILED_INIT; if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; } break; @@ -2779,10 +2762,10 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = CURLE_FAILED_INIT; else if(strlen(sshp->path) > 1) { sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); } else { - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); } break; @@ -2796,10 +2779,10 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, *sshc->slash_pos = 0; infof(data, "Creating directory '%s'", sshp->path); - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); break; } - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); break; case SSH_SFTP_CREATE_DIRS_MKDIR: @@ -2816,7 +2799,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = sshp ? sftp_readdir(data, sshc, sshp, block) : CURLE_FAILED_INIT; if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); } break; @@ -2838,11 +2821,11 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, if(result) { curlx_dyn_free(&sshp->readdir); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); } else { curlx_dyn_reset(&sshp->readdir); - myssh_state(data, sshc, SSH_SFTP_READDIR); + myssh_to(data, sshc, SSH_SFTP_READDIR); } break; @@ -2854,7 +2837,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, /* no data to transfer */ Curl_xfer_setup_nop(data); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); } break; @@ -2867,7 +2850,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = sshp ? sftp_download_stat(data, sshc, sshp, block) : CURLE_FAILED_INIT; if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; } break; @@ -2885,7 +2868,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = sshp ? Curl_getworkingpath(data, sshc->homedir, &sshp->path) : CURLE_FAILED_INIT; if(result) { - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; } @@ -2893,13 +2876,13 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, if(data->state.infilesize < 0) { failf(data, "SCP requires a known file size for upload"); result = CURLE_UPLOAD_FAILED; - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); break; } - myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT); + myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); } else { - myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT); + myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); } break; @@ -2915,9 +2898,9 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, case SSH_SCP_DONE: if(data->state.upload) - myssh_state(data, sshc, SSH_SCP_SEND_EOF); + myssh_to(data, sshc, SSH_SCP_SEND_EOF); else - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); break; case SSH_SCP_SEND_EOF: @@ -2935,7 +2918,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, rc, err_msg); } } - myssh_state(data, sshc, SSH_SCP_WAIT_EOF); + myssh_to(data, sshc, SSH_SCP_WAIT_EOF); break; case SSH_SCP_WAIT_EOF: @@ -2952,7 +2935,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); } } - myssh_state(data, sshc, SSH_SCP_WAIT_CLOSE); + myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE); break; case SSH_SCP_WAIT_CLOSE: @@ -2969,7 +2952,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, infof(data, "Channel failed to close: %d %s", rc, err_msg); } } - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); break; case SSH_SCP_CHANNEL_FREE: @@ -2989,7 +2972,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, sshc->ssh_channel = NULL; } CURL_TRC_SSH(data, "SCP DONE phase complete"); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; case SSH_SESSION_DISCONNECT: @@ -3004,13 +2987,13 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, memset(sshc, 0, sizeof(struct ssh_conn)); connclose(conn, "SSH session free"); sshc->state = SSH_SESSION_FREE; /* current */ - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; case SSH_QUIT: default: /* internal error */ - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; } @@ -3023,7 +3006,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = CURLE_OK; } CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - myssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), result, *block); return result; } @@ -3127,7 +3110,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data, if(result) break; - left_ms = Curl_timeleft_ms(data, FALSE); + left_ms = Curl_timeleft_ms(data); if(left_ms < 0) { failf(data, "Operation timed out"); return CURLE_OPERATION_TIMEDOUT; @@ -3235,7 +3218,7 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer, return -EAGAIN; /* magic return code for libssh2 */ else if(result) return -1; /* generic error */ - Curl_debug(data, CURLINFO_DATA_IN, (const char *)buffer, (size_t)nread); + Curl_debug(data, CURLINFO_DATA_IN, (const char *)buffer, nread); return (ssize_t)nread; } @@ -3400,7 +3383,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) } #endif /* CURL_DISABLE_PROXY */ - if(conn->handler->protocol & CURLPROTO_SCP) { + if(conn->scheme->protocol & CURLPROTO_SCP) { conn->recv[FIRSTSOCKET] = scp_recv; conn->send[FIRSTSOCKET] = scp_send; } @@ -3437,7 +3420,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) infof(data, "SSH socket: %d", (int)sock); #endif /* CURL_LIBSSH2_DEBUG */ - myssh_state(data, sshc, SSH_INIT); + myssh_to(data, sshc, SSH_INIT); result = ssh_multi_statemach(data, done); @@ -3467,7 +3450,7 @@ static CURLcode scp_perform(struct Curl_easy *data, return CURLE_FAILED_INIT; /* start the first command in the DO phase */ - myssh_state(data, sshc, SSH_SCP_TRANS_INIT); + myssh_to(data, sshc, SSH_SCP_TRANS_INIT); /* run the state-machine */ result = ssh_multi_statemach(data, dophase_done); @@ -3494,135 +3477,6 @@ static CURLcode scp_doing(struct Curl_easy *data, return result; } -/* - * The DO function is generic for both protocols. There was previously two - * separate ones but this way means less duplicated code. - */ - -static CURLcode ssh_do(struct Curl_easy *data, bool *done) -{ - CURLcode result; - bool connected = FALSE; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - - *done = FALSE; /* default to false */ - if(!sshc) - return CURLE_FAILED_INIT; - - data->req.size = -1; /* make sure this is unknown at this point */ - sshc->secondCreateDirs = 0; /* reset the create directory attempt state - variable */ - - Curl_pgrsReset(data); - - if(conn->handler->protocol & CURLPROTO_SCP) - result = scp_perform(data, &connected, done); - else - result = sftp_perform(data, &connected, done); - - return result; -} - -static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, - bool block) -{ - int rc; - - if(sshc->kh) { - libssh2_knownhost_free(sshc->kh); - sshc->kh = NULL; - } - - if(sshc->ssh_agent) { - rc = libssh2_agent_disconnect(sshc->ssh_agent); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to disconnect from libssh2 agent: %d %s", - rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - libssh2_agent_free(sshc->ssh_agent); - sshc->ssh_agent = NULL; - - /* NB: there is no need to free identities, they are part of internal - agent stuff */ - sshc->sshagent_identity = NULL; - sshc->sshagent_prev_identity = NULL; - } - - if(sshc->sftp_handle) { - rc = libssh2_sftp_close(sshc->sftp_handle); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - sshc->sftp_handle = NULL; - } - - if(sshc->ssh_channel) { - rc = libssh2_channel_free(sshc->ssh_channel); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 scp subsystem: %d %s", rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - sshc->ssh_channel = NULL; - } - - if(sshc->sftp_session) { - rc = libssh2_sftp_shutdown(sshc->sftp_session); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to stop libssh2 sftp subsystem: %d %s", rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - sshc->sftp_session = NULL; - } - - if(sshc->ssh_session) { - rc = libssh2_session_free(sshc->ssh_session); - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg); - } - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - sshc->ssh_session = NULL; - } - - /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->ssh_channel == NULL); - DEBUGASSERT(sshc->sftp_session == NULL); - DEBUGASSERT(sshc->sftp_handle == NULL); - DEBUGASSERT(sshc->kh == NULL); - DEBUGASSERT(sshc->ssh_agent == NULL); - - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - Curl_safefree(sshc->homedir); - - return CURLE_OK; -} - /* BLOCKING, but the function is using the state machine so the only reason this is still blocking is that the multi interface code has no support for disconnecting operations that takes a while */ @@ -3637,7 +3491,7 @@ static CURLcode scp_disconnect(struct Curl_easy *data, if(sshc && sshc->ssh_session) { /* only if there is a session still around to use! */ - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); result = ssh_block_statemach(data, sshc, sshp, TRUE); } @@ -3677,7 +3531,7 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status, (void)premature; if(sshc && !status) - myssh_state(data, sshc, SSH_SCP_DONE); + myssh_to(data, sshc, SSH_SCP_DONE); return ssh_done(data, status); } @@ -3769,7 +3623,7 @@ static CURLcode sftp_perform(struct Curl_easy *data, return CURLE_FAILED_INIT; /* start the first command in the DO phase */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT); + myssh_to(data, sshc, SSH_SFTP_QUOTE_INIT); /* run the state-machine */ result = ssh_multi_statemach(data, dophase_done); @@ -3810,7 +3664,7 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, if(sshc->ssh_session) { /* only if there is a session still around to use! */ CURL_TRC_SSH(data, "DISCONNECT starts now"); - myssh_state(data, sshc, SSH_SFTP_SHUTDOWN); + myssh_to(data, sshc, SSH_SFTP_SHUTDOWN); result = ssh_block_statemach(data, sshc, sshp, TRUE); CURL_TRC_SSH(data, "DISCONNECT is done -> %d", result); } @@ -3834,7 +3688,7 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, operation */ if(!premature && data->set.postquote && !conn->bits.retry) sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); } return ssh_done(data, status); } @@ -3897,70 +3751,33 @@ static CURLcode sftp_recv(struct Curl_easy *data, int sockindex, return CURLE_OK; } -static const char *sftp_libssh2_strerror(unsigned long err) +/* + * The DO function is generic for both protocols. There was previously two + * separate ones but this way means less duplicated code. + */ +static CURLcode ssh_do(struct Curl_easy *data, bool *done) { - switch(err) { - case LIBSSH2_FX_NO_SUCH_FILE: - return "No such file or directory"; - - case LIBSSH2_FX_PERMISSION_DENIED: - return "Permission denied"; - - case LIBSSH2_FX_FAILURE: - return "Operation failed"; - - case LIBSSH2_FX_BAD_MESSAGE: - return "Bad message from SFTP server"; - - case LIBSSH2_FX_NO_CONNECTION: - return "Not connected to SFTP server"; - - case LIBSSH2_FX_CONNECTION_LOST: - return "Connection to SFTP server lost"; - - case LIBSSH2_FX_OP_UNSUPPORTED: - return "Operation not supported by SFTP server"; - - case LIBSSH2_FX_INVALID_HANDLE: - return "Invalid handle"; - - case LIBSSH2_FX_NO_SUCH_PATH: - return "No such file or directory"; - - case LIBSSH2_FX_FILE_ALREADY_EXISTS: - return "File already exists"; - - case LIBSSH2_FX_WRITE_PROTECT: - return "File is write protected"; - - case LIBSSH2_FX_NO_MEDIA: - return "No media"; - - case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: - return "Disk full"; - - case LIBSSH2_FX_QUOTA_EXCEEDED: - return "User quota exceeded"; - - case LIBSSH2_FX_UNKNOWN_PRINCIPLE: - return "Unknown principle"; + CURLcode result; + bool connected = FALSE; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - case LIBSSH2_FX_LOCK_CONFlICT: - return "File lock conflict"; + *done = FALSE; /* default to false */ + if(!sshc) + return CURLE_FAILED_INIT; - case LIBSSH2_FX_DIR_NOT_EMPTY: - return "Directory not empty"; + data->req.size = -1; /* make sure this is unknown at this point */ + sshc->secondCreateDirs = 0; /* reset the create directory attempt state + variable */ - case LIBSSH2_FX_NOT_A_DIRECTORY: - return "Not a directory"; + Curl_pgrsReset(data); - case LIBSSH2_FX_INVALID_FILENAME: - return "Invalid filename"; + if(conn->scheme->protocol & CURLPROTO_SCP) + result = scp_perform(data, &connected, done); + else + result = sftp_perform(data, &connected, done); - case LIBSSH2_FX_LINK_LOOP: - return "Link points to itself"; - } - return "Unknown error in libssh2"; + return result; } CURLcode Curl_ssh_init(void) @@ -3974,7 +3791,7 @@ CURLcode Curl_ssh_init(void) void Curl_ssh_cleanup(void) { - (void)libssh2_exit(); + libssh2_exit(); } void Curl_ssh_version(char *buffer, size_t buflen) @@ -3990,7 +3807,7 @@ static void ssh_attach(struct Curl_easy *data, struct connectdata *conn) { DEBUGASSERT(data); DEBUGASSERT(conn); - if(conn->handler->protocol & PROTO_FAMILY_SSH) { + if(conn->scheme->protocol & PROTO_FAMILY_SSH) { struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); if(sshc && sshc->ssh_session) { /* only re-attach if the session already exists */ @@ -3999,4 +3816,51 @@ static void ssh_attach(struct Curl_easy *data, struct connectdata *conn) } } } + +/* + * SCP protocol handler. + */ +const struct Curl_protocol Curl_protocol_scp = { + ssh_setup_connection, /* setup_connection */ + ssh_do, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + ssh_connect, /* connect_it */ + ssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + ssh_pollset, /* proto_pollset */ + ssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ssh_pollset, /* perform_pollset */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ssh_attach, /* attach */ + ZERO_NULL, /* follow */ +}; + +/* + * SFTP protocol handler. + */ +const struct Curl_protocol Curl_protocol_sftp = { + ssh_setup_connection, /* setup_connection */ + ssh_do, /* do_it */ + sftp_done, /* done */ + ZERO_NULL, /* do_more */ + ssh_connect, /* connect_it */ + ssh_multi_statemach, /* connecting */ + sftp_doing, /* doing */ + ssh_pollset, /* proto_pollset */ + ssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + ssh_pollset, /* perform_pollset */ + sftp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_check */ + ssh_attach, /* attach */ + ZERO_NULL, /* follow */ +}; + #endif /* USE_LIBSSH2 */ diff --git a/vendor/curl/lib/vssh/ssh.h b/vendor/curl/lib/vssh/ssh.h index 6fdf3f7..1afb1e8 100644 --- a/vendor/curl/lib/vssh/ssh.h +++ b/vendor/curl/lib/vssh/ssh.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_SSH_H -#define HEADER_CURL_SSH_H +#ifndef HEADER_CURL_VSSH_SSH_H +#define HEADER_CURL_VSSH_SSH_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,7 +23,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" +#include "urldata.h" + +extern const struct Curl_protocol Curl_protocol_sftp; +extern const struct Curl_protocol Curl_protocol_scp; + +extern const struct Curl_scheme Curl_scheme_sftp; +extern const struct Curl_scheme Curl_scheme_scp; + +#ifdef USE_SSH #ifdef USE_LIBSSH2 #include @@ -230,10 +239,16 @@ struct ssh_conn { #endif /* USE_LIBSSH2 */ -#ifdef USE_SSH +#ifdef CURLVERBOSE +const char *Curl_ssh_statename(sshstate state); +#else +#define Curl_ssh_statename(x) "" +#endif +void Curl_ssh_set_state(struct Curl_easy *data, + struct ssh_conn *sshc, + sshstate nowstate); -extern const struct Curl_handler Curl_handler_scp; -extern const struct Curl_handler Curl_handler_sftp; +#define myssh_to(x, y, z) Curl_ssh_set_state(x, y, z) /* generic SSH backend functions */ CURLcode Curl_ssh_init(void); @@ -241,11 +256,10 @@ void Curl_ssh_cleanup(void); void Curl_ssh_version(char *buffer, size_t buflen); void Curl_ssh_attach(struct Curl_easy *data, struct connectdata *conn); -#else -/* for non-SSH builds */ +#else /* !USE_SSH */ #define Curl_ssh_cleanup() #define Curl_ssh_attach(x, y) #define Curl_ssh_init() 0 -#endif +#endif /* USE_SSH */ #endif /* HEADER_CURL_SSH_H */ diff --git a/vendor/curl/lib/vssh/vssh.c b/vendor/curl/lib/vssh/vssh.c index 77fa89b..d499a63 100644 --- a/vendor/curl/lib/vssh/vssh.c +++ b/vendor/curl/lib/vssh/vssh.c @@ -21,20 +21,112 @@ * SPDX-License-Identifier: curl AND ISC * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" +#include "vssh/ssh.h" #ifdef USE_SSH -#include "vssh.h" -#include "../curlx/strparse.h" -#include "../curl_trc.h" -#include "../escape.h" +#include "vssh/vssh.h" +#include "curlx/strparse.h" +#include "curl_trc.h" +#include "escape.h" + +#ifdef CURLVERBOSE +const char *Curl_ssh_statename(sshstate state) +{ + static const char * const names[] = { + "SSH_STOP", + "SSH_INIT", + "SSH_S_STARTUP", + "SSH_HOSTKEY", + "SSH_AUTHLIST", + "SSH_AUTH_PKEY_INIT", + "SSH_AUTH_PKEY", + "SSH_AUTH_PASS_INIT", + "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", + "SSH_AUTH_HOST_INIT", + "SSH_AUTH_HOST", + "SSH_AUTH_KEY_INIT", + "SSH_AUTH_KEY", + "SSH_AUTH_GSSAPI", + "SSH_AUTH_DONE", + "SSH_SFTP_INIT", + "SSH_SFTP_REALPATH", + "SSH_SFTP_QUOTE_INIT", + "SSH_SFTP_POSTQUOTE_INIT", + "SSH_SFTP_QUOTE", + "SSH_SFTP_NEXT_QUOTE", + "SSH_SFTP_QUOTE_STAT", + "SSH_SFTP_QUOTE_SETSTAT", + "SSH_SFTP_QUOTE_SYMLINK", + "SSH_SFTP_QUOTE_MKDIR", + "SSH_SFTP_QUOTE_RENAME", + "SSH_SFTP_QUOTE_RMDIR", + "SSH_SFTP_QUOTE_UNLINK", + "SSH_SFTP_QUOTE_STATVFS", + "SSH_SFTP_GETINFO", + "SSH_SFTP_FILETIME", + "SSH_SFTP_TRANS_INIT", + "SSH_SFTP_UPLOAD_INIT", + "SSH_SFTP_CREATE_DIRS_INIT", + "SSH_SFTP_CREATE_DIRS", + "SSH_SFTP_CREATE_DIRS_MKDIR", + "SSH_SFTP_READDIR_INIT", + "SSH_SFTP_READDIR", + "SSH_SFTP_READDIR_LINK", + "SSH_SFTP_READDIR_BOTTOM", + "SSH_SFTP_READDIR_DONE", + "SSH_SFTP_DOWNLOAD_INIT", + "SSH_SFTP_DOWNLOAD_STAT", + "SSH_SFTP_CLOSE", + "SSH_SFTP_SHUTDOWN", + "SSH_SCP_TRANS_INIT", + "SSH_SCP_UPLOAD_INIT", + "SSH_SCP_DOWNLOAD_INIT", + "SSH_SCP_DOWNLOAD", + "SSH_SCP_DONE", + "SSH_SCP_SEND_EOF", + "SSH_SCP_WAIT_EOF", + "SSH_SCP_WAIT_CLOSE", + "SSH_SCP_CHANNEL_FREE", + "SSH_SESSION_DISCONNECT", + "SSH_SESSION_FREE", + "QUIT" + }; + /* a precaution to make sure the lists are in sync */ + DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST); + return ((size_t)state < CURL_ARRAYSIZE(names)) ? names[state] : ""; +} +#endif /* CURLVERBOSE */ + +/* + * SSH State machine related code + */ +/* This is the ONLY way to change SSH state! */ +void Curl_ssh_set_state(struct Curl_easy *data, + struct ssh_conn *sshc, + sshstate nowstate) +{ +#ifdef CURLVERBOSE + if(sshc->state != nowstate) { + CURL_TRC_SSH(data, "[%s] -> [%s]", + Curl_ssh_statename(sshc->state), + Curl_ssh_statename(nowstate)); + } +#else + (void)data; +#endif + sshc->state = nowstate; +} #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 */ + const char *homedir, /* when SFTP is used */ char **path) /* returns the allocated real path to work with */ { @@ -51,7 +143,7 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data, curlx_dyn_init(&npath, MAX_SSHPATH_LEN); /* Check for /~/, indicating relative to the user's home directory */ - if((data->conn->handler->protocol & CURLPROTO_SCP) && + if((data->conn->scheme->protocol & CURLPROTO_SCP) && (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) { /* It is referenced to the home directory, so strip the leading '/~/' */ if(curlx_dyn_addn(&npath, &working_path[3], working_path_len - 3)) { @@ -59,7 +151,7 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } } - else if((data->conn->handler->protocol & CURLPROTO_SFTP) && + else if((data->conn->scheme->protocol & CURLPROTO_SFTP) && (!strcmp("/~", working_path) || ((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) { if(curlx_dyn_add(&npath, homedir)) { @@ -193,19 +285,19 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir) } CURLcode Curl_ssh_range(struct Curl_easy *data, - const char *p, curl_off_t filesize, + const char *range, curl_off_t filesize, curl_off_t *startp, curl_off_t *sizep) { curl_off_t from, to; int to_t; - int from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX); + int from_t = curlx_str_number(&range, &from, CURL_OFF_T_MAX); if(from_t == STRE_OVERFLOW) return CURLE_RANGE_ERROR; - curlx_str_passblanks(&p); - (void)curlx_str_single(&p, '-'); + curlx_str_passblanks(&range); + (void)curlx_str_single(&range, '-'); - to_t = curlx_str_numblanks(&p, &to); - if((to_t == STRE_OVERFLOW) || (to_t && from_t) || *p) + to_t = curlx_str_numblanks(&range, &to); + if((to_t == STRE_OVERFLOW) || (to_t && from_t) || *range) return CURLE_RANGE_ERROR; if(from_t) { @@ -239,3 +331,34 @@ CURLcode Curl_ssh_range(struct Curl_easy *data, } #endif /* USE_SSH */ + +/* + * SFTP protocol handler. + */ +const struct Curl_scheme Curl_scheme_sftp = { + "SFTP", /* scheme */ +#ifndef USE_SSH + NULL, +#else + &Curl_protocol_sftp, +#endif + CURLPROTO_SFTP, /* protocol */ + CURLPROTO_SFTP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE, + PORT_SSH /* defport */ +}; + +const struct Curl_scheme Curl_scheme_scp = { + "SCP", /* scheme */ +#ifndef USE_SSH + NULL, +#else + &Curl_protocol_scp, +#endif + CURLPROTO_SCP, /* protocol */ + CURLPROTO_SCP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE, + PORT_SSH, /* defport */ +}; diff --git a/vendor/curl/lib/vssh/vssh.h b/vendor/curl/lib/vssh/vssh.h index 4bc6d80..492108f 100644 --- a/vendor/curl/lib/vssh/vssh.h +++ b/vendor/curl/lib/vssh/vssh.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_PATH_H -#define HEADER_CURL_PATH_H +#ifndef HEADER_CURL_VSSH_VSSH_H +#define HEADER_CURL_VSSH_VSSH_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,12 +23,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#include "../urldata.h" +#ifdef USE_SSH + +#include "urldata.h" CURLcode Curl_getworkingpath(struct Curl_easy *data, - char *homedir, + const char *homedir, char **path); CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir); @@ -36,5 +38,5 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir); CURLcode Curl_ssh_range(struct Curl_easy *data, const char *range, curl_off_t filesize, curl_off_t *startp, curl_off_t *sizep); - -#endif /* HEADER_CURL_PATH_H */ +#endif /* USE_SSH */ +#endif /* HEADER_CURL_VSSH_VSSH_H */ diff --git a/vendor/curl/lib/vtls/apple.c b/vendor/curl/lib/vtls/apple.c index 9779e11..a6a5ba0 100644 --- a/vendor/curl/lib/vtls/apple.c +++ b/vendor/curl/lib/vtls/apple.c @@ -38,21 +38,17 @@ https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html */ -#include "../curl_setup.h" - -#include "../urldata.h" -#include "../cfilters.h" -#include "../curl_trc.h" -#include "vtls.h" -#include "apple.h" +#include "curl_setup.h" #ifdef USE_APPLE_SECTRUST -#include -#endif +#include "urldata.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "vtls/vtls.h" +#include "vtls/apple.h" -#ifdef USE_APPLE_SECTRUST -#define SSL_SYSTEM_VERIFIER +#include #if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && \ MAC_OS_X_VERSION_MAX_ALLOWED >= 101400) || \ @@ -233,7 +229,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, result = SecTrustEvaluateWithError(trust, &error) ? CURLE_OK : CURLE_PEER_FAILED_VERIFICATION; if(error) { - CFIndex code = CFErrorGetCode(error); + VERBOSE(CFIndex code = CFErrorGetCode(error)); error_ref = CFErrorCopyDescription(error); if(error_ref) { diff --git a/vendor/curl/lib/vtls/apple.h b/vendor/curl/lib/vtls/apple.h index dd1b5db..d86a56b 100644 --- a/vendor/curl/lib/vtls/apple.h +++ b/vendor/curl/lib/vtls/apple.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_APPLE_SECTRUST struct Curl_cfilter; diff --git a/vendor/curl/lib/vtls/cipher_suite.c b/vendor/curl/lib/vtls/cipher_suite.c index 562a347..1fc6e9f 100644 --- a/vendor/curl/lib/vtls/cipher_suite.c +++ b/vendor/curl/lib/vtls/cipher_suite.c @@ -21,11 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_MBEDTLS) || defined(USE_RUSTLS) -#include "cipher_suite.h" +#include "vtls/cipher_suite.h" /* * To support the CURLOPT_SSL_CIPHER_LIST option on SSL backends @@ -159,13 +159,11 @@ struct cs_entry { /* !checksrc! disable COMMANOSPACE all */ static const struct cs_entry cs_list[] = { /* TLS 1.3 ciphers */ -#if defined(USE_MBEDTLS) || defined(USE_RUSTLS) CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,), CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,), CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,), CS_ENTRY(0x1304, TLS,AES,128,CCM,SHA256,,,), CS_ENTRY(0x1305, TLS,AES,128,CCM,8,SHA256,,), -#endif /* TLS 1.2 ciphers */ CS_ENTRY(0xC02B, TLS,ECDHE,ECDSA,WITH,AES,128,GCM,SHA256), CS_ENTRY(0xC02B, ECDHE,ECDSA,AES128,GCM,SHA256,,,), @@ -232,8 +230,6 @@ static const struct cs_entry cs_list[] = { CS_ENTRY(0xC031, ECDH,RSA,AES128,GCM,SHA256,,,), CS_ENTRY(0xC032, TLS,ECDH,RSA,WITH,AES,256,GCM,SHA384), CS_ENTRY(0xC032, ECDH,RSA,AES256,GCM,SHA384,,,), -#endif -#ifdef USE_MBEDTLS CS_ENTRY(0x0001, TLS,RSA,WITH,NULL,MD5,,,), CS_ENTRY(0x0001, NULL,MD5,,,,,,), CS_ENTRY(0x0002, TLS,RSA,WITH,NULL,SHA,,,), @@ -320,8 +316,6 @@ static const struct cs_entry cs_list[] = { CS_ENTRY(0xC036, ECDHE,PSK,AES256,CBC,SHA,,,), CS_ENTRY(0xCCAB, TLS,PSK,WITH,CHACHA20,POLY1305,SHA256,,), CS_ENTRY(0xCCAB, PSK,CHACHA20,POLY1305,,,,,), -#endif -#ifdef USE_MBEDTLS CS_ENTRY(0xC09C, TLS,RSA,WITH,AES,128,CCM,,), CS_ENTRY(0xC09C, AES128,CCM,,,,,,), CS_ENTRY(0xC09D, TLS,RSA,WITH,AES,256,CCM,,), @@ -338,8 +332,6 @@ static const struct cs_entry cs_list[] = { CS_ENTRY(0xC0AE, ECDHE,ECDSA,AES128,CCM8,,,,), CS_ENTRY(0xC0AF, TLS,ECDHE,ECDSA,WITH,AES,256,CCM,8), CS_ENTRY(0xC0AF, ECDHE,ECDSA,AES256,CCM8,,,,), -#endif -#ifdef USE_MBEDTLS /* entries marked ns are "non-standard", they are not in OpenSSL */ CS_ENTRY(0x0041, TLS,RSA,WITH,CAMELLIA,128,CBC,SHA,), CS_ENTRY(0x0041, CAMELLIA128,SHA,,,,,,), diff --git a/vendor/curl/lib/vtls/cipher_suite.h b/vendor/curl/lib/vtls/cipher_suite.h index a17284f..0e185e3 100644 --- a/vendor/curl/lib/vtls/cipher_suite.h +++ b/vendor/curl/lib/vtls/cipher_suite.h @@ -23,10 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_MBEDTLS) || defined(USE_RUSTLS) -#include /* Lookup IANA id for cipher suite string, returns 0 if not recognized */ uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len); diff --git a/vendor/curl/lib/vtls/gtls.c b/vendor/curl/lib/vtls/gtls.c index 934273e..a0a465c 100644 --- a/vendor/curl/lib/vtls/gtls.c +++ b/vendor/curl/lib/vtls/gtls.c @@ -28,7 +28,7 @@ * Note: do not use the GnuTLS' *_t variable type names in this source code, * since they were not present in 1.0.X. */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_GNUTLS @@ -38,24 +38,26 @@ #include #include -#include "../urldata.h" -#include "../curl_trc.h" -#include "keylog.h" -#include "gtls.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "apple.h" -#include "../vauth/vauth.h" -#include "../parsedate.h" -#include "../connect.h" /* for the connect timeout */ -#include "../progress.h" -#include "../strdup.h" -#include "../curlx/fopen.h" -#include "x509asn1.h" +#include "urldata.h" +#include "curl_trc.h" +#include "vtls/keylog.h" +#include "vtls/gtls.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/apple.h" +#include "vauth/vauth.h" +#include "parsedate.h" +#include "connect.h" /* for the connect timeout */ +#include "progress.h" +#include "curlx/strdup.h" +#include "curlx/fopen.h" +#include "vtls/x509asn1.h" /* Enable GnuTLS debugging by defining GTLSDEBUG */ -/*#define GTLSDEBUG */ +#if 0 +#define GTLSDEBUG +#endif #ifdef GTLSDEBUG static void tls_log_func(int level, const char *str) @@ -97,7 +99,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen) if(result) { /* !checksrc! disable ERRNOVAR 1 */ gnutls_transport_set_errno(backend->gtls.session, - (CURLE_AGAIN == result) ? EAGAIN : EINVAL); + (result == CURLE_AGAIN) ? EAGAIN : EINVAL); return -1; } return (ssize_t)nwritten; @@ -130,7 +132,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) if(result) { /* !checksrc! disable ERRNOVAR 1 */ gnutls_transport_set_errno(backend->gtls.session, - (CURLE_AGAIN == result) ? EAGAIN : EINVAL); + (result == CURLE_AGAIN) ? EAGAIN : EINVAL); return -1; } else if(nread == 0) @@ -166,7 +168,7 @@ static void gtls_cleanup(void) Curl_tls_keylog_close(); } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE static void showtime(struct Curl_easy *data, const char *text, time_t stamp) { struct tm buffer; @@ -323,8 +325,8 @@ gnutls_set_ssl_version_min_max(struct Curl_easy *data, long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; - if((ssl_version == CURL_SSLVERSION_DEFAULT) || - (ssl_version == CURL_SSLVERSION_TLSv1)) + DEBUGASSERT(ssl_version != CURL_SSLVERSION_DEFAULT); + if(ssl_version <= CURL_SSLVERSION_TLSv1) ssl_version = CURL_SSLVERSION_TLSv1_0; if((ssl_version_max == CURL_SSLVERSION_MAX_NONE) || (ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT)) @@ -723,7 +725,7 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, "and store in cache", sdata_len, alpn ? alpn : "-", earlydata_max); if(quic_tp && quic_tp_len) { - qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len); + qtp_clone = curlx_memdup0((const char *)quic_tp, quic_tp_len); if(!qtp_clone) { curlx_free(sdata); return CURLE_OUT_OF_MEMORY; @@ -1079,29 +1081,14 @@ static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; - CURLcode result = CURLE_OK; - *do_early_data = FALSE; connssl->earlydata_max = gnutls_record_get_max_early_data_size(backend->gtls.session); - if((!connssl->earlydata_max || connssl->earlydata_max == 0xFFFFFFFFUL)) { - /* Seems to be no GnuTLS way to signal no EarlyData in session */ - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); - } - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); - connssl->earlydata_state = ssl_earlydata_await; - connssl->state = ssl_connection_deferred; - result = Curl_alpn_set_negotiated(cf, data, connssl, - (const unsigned char *)scs->alpn, - scs->alpn ? strlen(scs->alpn) : 0); - *do_early_data = !result; - } - return result; + + /* Seems to be no GnuTLS way to signal no EarlyData in session */ + return Curl_on_session_reuse(cf, data, alpns, scs, do_early_data, + connssl->earlydata_max && + connssl->earlydata_max != 0xFFFFFFFFUL); } #endif @@ -1326,7 +1313,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, void Curl_gtls_report_handshake(struct Curl_easy *data, struct gtls_ctx *gctx) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { const char *ptr; gnutls_protocol_t version = gnutls_protocol_get_version(gctx->session); @@ -1379,7 +1366,7 @@ static void gtls_msg_verify_result(struct Curl_easy *data, static void gtls_infof_cert(struct Curl_easy *data, gnutls_x509_crt_t x509_cert) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { gnutls_datum_t certfields; int rc, algo; @@ -1572,8 +1559,6 @@ static CURLcode glts_apple_verify(struct Curl_cfilter *cf, result = Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, gtls_chain_get_der, chain, NULL, 0); *pverified = !result; - if(*pverified) - infof(data, " SSL certificate verified by Apple SecTrust."); return result; } #endif /* USE_APPLE_SECTRUST */ @@ -2069,7 +2054,6 @@ static CURLcode gtls_send(struct Curl_cfilter *cf, ssize_t nwritten; size_t remain = blen; - (void)data; DEBUGASSERT(backend); *pnwritten = 0; @@ -2186,7 +2170,6 @@ static void gtls_close(struct Curl_cfilter *cf, struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; - (void)data; DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "close"); if(backend->gtls.session) { @@ -2215,7 +2198,6 @@ static CURLcode gtls_recv(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; ssize_t nread; - (void)data; DEBUGASSERT(backend); nread = gnutls_record_recv(backend->gtls.session, buf, blen); @@ -2299,7 +2281,9 @@ const struct Curl_ssl Curl_ssl_gnutls = { SSLSUPP_HTTPS_PROXY | SSLSUPP_CAINFO_BLOB | SSLSUPP_CIPHER_LIST | - SSLSUPP_CA_CACHE, + SSLSUPP_CA_CACHE | + SSLSUPP_ISSUERCERT | + SSLSUPP_CRLFILE, sizeof(struct gtls_ssl_backend_data), diff --git a/vendor/curl/lib/vtls/gtls.h b/vendor/curl/lib/vtls/gtls.h index ecdca0a..a5e55cd 100644 --- a/vendor/curl/lib/vtls/gtls.h +++ b/vendor/curl/lib/vtls/gtls.h @@ -23,12 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_GNUTLS #include -#include "../curlx/timeval.h" + +#include "curlx/timeval.h" #ifdef HAVE_GNUTLS_SRP /* the function exists */ diff --git a/vendor/curl/lib/vtls/hostcheck.c b/vendor/curl/lib/vtls/hostcheck.c index 9706336..ad09b83 100644 --- a/vendor/curl/lib/vtls/hostcheck.c +++ b/vendor/curl/lib/vtls/hostcheck.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_OPENSSL) || defined(USE_SCHANNEL) /* these backends use functions from this file */ @@ -32,9 +32,10 @@ #ifdef HAVE_NETINET_IN6_H #include #endif -#include "../curl_memrchr.h" -#include "hostcheck.h" -#include "../hostip.h" + +#include "curl_memrchr.h" +#include "vtls/hostcheck.h" +#include "hostip.h" /* check the two input strings with given length, but do not assume they end in nul-bytes */ diff --git a/vendor/curl/lib/vtls/hostcheck.h b/vendor/curl/lib/vtls/hostcheck.h index d62c721..d8e1a52 100644 --- a/vendor/curl/lib/vtls/hostcheck.h +++ b/vendor/curl/lib/vtls/hostcheck.h @@ -23,12 +23,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_OPENSSL) || defined(USE_SCHANNEL) /* returns TRUE if there is a match */ -bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen, +bool Curl_cert_hostcheck(const char *match, size_t matchlen, const char *hostname, size_t hostlen); #endif diff --git a/vendor/curl/lib/vtls/keylog.c b/vendor/curl/lib/vtls/keylog.c index ace9cab..0206e32 100644 --- a/vendor/curl/lib/vtls/keylog.c +++ b/vendor/curl/lib/vtls/keylog.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_OPENSSL) || \ defined(USE_GNUTLS) || \ @@ -30,9 +30,9 @@ defined(USE_QUICHE) || \ defined(USE_RUSTLS) -#include "keylog.h" -#include "../escape.h" -#include "../curlx/fopen.h" +#include "vtls/keylog.h" +#include "escape.h" +#include "curlx/fopen.h" /* The fp for the open SSLKEYLOGFILE, or NULL if not open */ static FILE *keylog_file_fp; @@ -107,8 +107,9 @@ bool Curl_tls_keylog_write(const char *label, const unsigned char *secret, size_t secretlen) { size_t pos, i; - unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 + - 2 * SECRET_MAXLEN + 1 + 1]; + unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + + (2 * CLIENT_RANDOM_SIZE) + 1 + + (2 * SECRET_MAXLEN) + 1 + 1]; if(!keylog_file_fp) { return FALSE; diff --git a/vendor/curl/lib/vtls/keylog.h b/vendor/curl/lib/vtls/keylog.h index ec82abf..7e741b9 100644 --- a/vendor/curl/lib/vtls/keylog.h +++ b/vendor/curl/lib/vtls/keylog.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) diff --git a/vendor/curl/lib/vtls/mbedtls.c b/vendor/curl/lib/vtls/mbedtls.c index dff45a3..2bac406 100644 --- a/vendor/curl/lib/vtls/mbedtls.c +++ b/vendor/curl/lib/vtls/mbedtls.c @@ -27,7 +27,7 @@ * but vtls.c should ever call or use these functions. * */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_MBEDTLS @@ -56,17 +56,18 @@ #ifdef MBEDTLS_DEBUG #include #endif -#include "cipher_suite.h" -#include "../urldata.h" -#include "../curl_trc.h" -#include "mbedtls.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "x509asn1.h" -#include "../connect.h" /* for the connect timeout */ -#include "../strdup.h" -#include "../curl_sha256.h" + +#include "vtls/cipher_suite.h" +#include "urldata.h" +#include "curl_trc.h" +#include "vtls/mbedtls.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/x509asn1.h" +#include "connect.h" /* for the connect timeout */ +#include "curlx/strdup.h" +#include "curl_sha256.h" /* ALPN for http2 */ #if defined(USE_HTTP2) && defined(MBEDTLS_SSL_ALPN) @@ -141,7 +142,7 @@ static int mbedtls_bio_cf_write(void *bio, result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %d, %zu", blen, result, nwritten); - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_WRITE; return result ? -1 : (int)nwritten; } @@ -163,7 +164,7 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) result = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &nread); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %d, %zu", blen, result, nread); - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_READ; /* nread is never larger than int here */ return result ? -1 : (int)nread; @@ -172,8 +173,8 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) /* See: * https://web.archive.org/web/20200921194007/tls.mbed.org/discussions/generic/howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der */ -#define RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE) -#define ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES) +#define RSA_PUB_DER_MAX_BYTES (38 + (2 * MBEDTLS_MPI_MAX_SIZE)) +#define ECP_PUB_DER_MAX_BYTES (30 + (2 * MBEDTLS_ECP_MAX_BYTES)) #define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) @@ -198,8 +199,8 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, #endif ; + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -245,10 +246,10 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, return CURLE_OK; } -/* TLS_ECJPAKE_WITH_AES_128_CCM_8 (0xC0FF) is marked experimental - in mbedTLS. The number is not reserved by IANA nor is the - cipher suite present in other SSL implementations. Provide - provisional support for specifying the cipher suite here. */ +/* TLS_ECJPAKE_WITH_AES_128_CCM_8 (0xC0FF) is marked experimental in mbedTLS. + The number is not reserved by IANA nor is the cipher suite present in other + SSL implementations. Provide provisional support for specifying the cipher + suite here. */ #ifdef MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 static int mbed_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, bool prefer_rfc) @@ -389,7 +390,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, static void mbed_dump_cert_info(struct Curl_easy *data, const mbedtls_x509_crt *crt) { -#if defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(MBEDTLS_X509_REMOVE_INFO) +#if !defined(CURLVERBOSE) || defined(MBEDTLS_X509_REMOVE_INFO) (void)data, (void)crt; #else const size_t bufsize = 16384; @@ -468,62 +469,46 @@ static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt, return 0; } -static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ (ca_info_blob ? NULL : conn_config->CAfile); const bool verifypeer = conn_config->verifypeer; const char * const ssl_capath = conn_config->CApath; - char * const ssl_cert = ssl_config->primary.clientcert; - const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; #ifdef MBEDTLS_PEM_PARSE_C + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); const char * const ssl_cert_type = ssl_config->cert_type; #endif - const char * const ssl_crlfile = ssl_config->primary.CRLfile; - const char *hostname = connssl->peer.hostname; int ret = -1; char errorbuf[128]; - DEBUGASSERT(backend); - DEBUGASSERT(!backend->initialized); - - if((conn_config->version == CURL_SSLVERSION_SSLv2) || - (conn_config->version == CURL_SSLVERSION_SSLv3)) { - failf(data, "Not supported SSL version"); - return CURLE_NOT_BUILT_IN; - } - - /* Load the trusted CA */ mbedtls_x509_crt_init(&backend->cacert); if(ca_info_blob && verifypeer) { #ifdef MBEDTLS_PEM_PARSE_C - /* if DER or a null-terminated PEM just process using + /* if DER or a null-terminated PEM process using mbedtls_x509_crt_parse(). */ if((ssl_cert_type && curl_strequal(ssl_cert_type, "DER")) || ((char *)(ca_info_blob->data))[ca_info_blob->len - 1] == '\0') { - - ret = mbedtls_x509_crt_parse(&backend->cacert, - ca_info_blob->data, + ret = mbedtls_x509_crt_parse(&backend->cacert, ca_info_blob->data, ca_info_blob->len); } else { /* they say it is PEM and it is not null-terminated */ - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to - be null-terminated if the data is PEM encoded (even when - provided the exact length). The function accepts PEM or DER - formats, but we cannot assume if the user passed in a PEM - format cert that it is null-terminated. */ - unsigned char *newblob = Curl_memdup0(ca_info_blob->data, - ca_info_blob->len); + /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be + null-terminated if the data is PEM encoded (even when provided the + exact length). The function accepts PEM or DER formats, but we cannot + assume if the user passed in a PEM format cert that it is + null-terminated. */ + unsigned char *newblob = curlx_memdup0(ca_info_blob->data, + ca_info_blob->len); if(!newblob) return CURLE_OUT_OF_MEMORY; @@ -532,12 +517,10 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, curlx_free(newblob); } #else - /* DER encoded certs do not need to be null terminated - because it is a binary format. So if we are not compiling - with PEM_PARSE we can avoid the extra memory copies - altogether. */ - ret = mbedtls_x509_crt_parse_der(&backend->cacert, - ca_info_blob->data, + /* DER encoded certs do not need to be null terminated because it is a + binary format. So if we are not compiling with PEM_PARSE we can avoid + the extra memory copies altogether. */ + ret = mbedtls_x509_crt_parse_der(&backend->cacert, ca_info_blob->data, ca_info_blob->len); #endif @@ -583,7 +566,24 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, #endif } - /* Load the client certificate */ + return CURLE_OK; +} + +static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + char * const ssl_cert = ssl_config->primary.clientcert; + const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; +#ifdef MBEDTLS_PEM_PARSE_C + const char * const ssl_cert_type = ssl_config->cert_type; +#endif + int ret = -1; + char errorbuf[128]; + mbedtls_x509_crt_init(&backend->clicert); if(ssl_cert) { @@ -605,7 +605,7 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, if(ssl_cert_blob) { #ifdef MBEDTLS_PEM_PARSE_C - /* if DER or a null-terminated PEM just process using + /* if DER or a null-terminated PEM process using mbedtls_x509_crt_parse(). */ if((ssl_cert_type && curl_strequal(ssl_cert_type, "DER")) || ((char *)(ssl_cert_blob->data))[ssl_cert_blob->len - 1] == '\0') { @@ -616,13 +616,13 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, } else { /* they say it is PEM and it is not null-terminated */ - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to - be null-terminated if the data is PEM encoded (even when - provided the exact length). The function accepts PEM or DER - formats, but we cannot assume if the user passed in a PEM - format cert that it is null-terminated. */ - unsigned char *newblob = Curl_memdup0(ssl_cert_blob->data, - ssl_cert_blob->len); + /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be + null-terminated if the data is PEM encoded (even when provided the + exact length). The function accepts PEM or DER formats, but we cannot + assume if the user passed in a PEM format cert that it is + null-terminated. */ + unsigned char *newblob = curlx_memdup0(ssl_cert_blob->data, + ssl_cert_blob->len); if(!newblob) return CURLE_OUT_OF_MEMORY; ret = mbedtls_x509_crt_parse(&backend->clicert, newblob, @@ -630,24 +630,34 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, curlx_free(newblob); } #else - /* DER encoded certs do not need to be null terminated - because it is a binary format. So if we are not compiling - with PEM_PARSE we can avoid the extra memory copies - altogether. */ - ret = mbedtls_x509_crt_parse_der(&backend->clicert, - ssl_cert_blob->data, + /* DER encoded certs do not need to be null terminated because it is a + binary format. So if we are not compiling with PEM_PARSE we can avoid + the extra memory copies altogether. */ + ret = mbedtls_x509_crt_parse_der(&backend->clicert, ssl_cert_blob->data, ssl_cert_blob->len); #endif if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedTLS: error reading client cert data %s: (-0x%04X) %s", - ssl_config->key, -ret, errorbuf); + failf(data, "mbedTLS: error reading client cert blob: (-0x%04X) %s", + -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } - /* Load the client private key */ + return CURLE_OK; +} + +static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + int ret = -1; + char errorbuf[128]; + mbedtls_pk_init(&backend->pk); if(ssl_config->key || ssl_config->key_blob) { @@ -656,12 +666,13 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, #if MBEDTLS_VERSION_NUMBER >= 0x04000000 ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, ssl_config->key_passwd); - if(ret == 0 && !(mbedtls_pk_can_do_psa(&backend->pk, - PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), - PSA_KEY_USAGE_SIGN_HASH) || - mbedtls_pk_can_do_psa(&backend->pk, - MBEDTLS_PK_ALG_ECDSA(PSA_ALG_ANY_HASH), - PSA_KEY_USAGE_SIGN_HASH))) + if(ret == 0 && + !(mbedtls_pk_can_do_psa(&backend->pk, + PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), + PSA_KEY_USAGE_SIGN_HASH) || + mbedtls_pk_can_do_psa(&backend->pk, + MBEDTLS_PK_ALG_ECDSA(PSA_ALG_ANY_HASH), + PSA_KEY_USAGE_SIGN_HASH))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #else ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, @@ -693,12 +704,13 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0); - if(ret == 0 && !(mbedtls_pk_can_do_psa(&backend->pk, - PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), - PSA_KEY_USAGE_SIGN_HASH) || - mbedtls_pk_can_do_psa(&backend->pk, - MBEDTLS_PK_ALG_ECDSA(PSA_ALG_ANY_HASH), - PSA_KEY_USAGE_SIGN_HASH))) + if(ret == 0 && + !(mbedtls_pk_can_do_psa(&backend->pk, + PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), + PSA_KEY_USAGE_SIGN_HASH) || + mbedtls_pk_can_do_psa(&backend->pk, + MBEDTLS_PK_ALG_ECDSA(PSA_ALG_ANY_HASH), + PSA_KEY_USAGE_SIGN_HASH))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #else ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, @@ -720,13 +732,25 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, } } - /* Load the CRL */ + return CURLE_OK; +} + +static CURLcode mbed_load_crl(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + const char * const ssl_crlfile = ssl_config->primary.CRLfile; + #ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl_init(&backend->crl); if(ssl_crlfile) { + char errorbuf[128]; #ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); + int ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); @@ -736,18 +760,36 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CRL_BADFILE; } #else + (void)errorbuf; failf(data, "mbedTLS: functions that use the file system not built in"); return CURLE_NOT_BUILT_IN; #endif } #else + (void)backend; if(ssl_crlfile) { failf(data, "mbedTLS: CRL support not built in"); return CURLE_NOT_BUILT_IN; } #endif - infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->peer.port); + return CURLE_OK; +} + +static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + int ret; + CURLcode result; + char errorbuf[128]; + + infof(data, "mbedTLS: Connecting to %s:%d", + connssl->peer.hostname, connssl->peer.port); mbedtls_ssl_config_init(&backend->config); ret = mbedtls_ssl_config_defaults(&backend->config, @@ -772,11 +814,13 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, #endif #if defined(MBEDTLS_SSL_SESSION_TICKETS) && \ - MBEDTLS_VERSION_NUMBER >= 0x03060100 && MBEDTLS_VERSION_NUMBER < 0x04000000 - /* New in mbedTLS 3.6.1, need to enable, default is now disabled. - 4.0.0 enabled it by default for TLSv1.3. */ - mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config, - MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); + defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + MBEDTLS_VERSION_NUMBER >= 0x03060100 && \ + MBEDTLS_VERSION_NUMBER < 0x04000000 + /* New in mbedTLS 3.6.1, need to enable, default is now disabled. 4.0.0 + enabled it by default for TLSv1.3. */ + mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets( + &backend->config, MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); #endif /* Always let mbedTLS verify certificates, if verifypeer or verifyhost are @@ -792,20 +836,21 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, mbedtls_ssl_conf_cert_profile(&backend->config, &mbedtls_x509_crt_profile_next); - ret = mbed_set_ssl_version_min_max(data, backend, conn_config); - if(ret != CURLE_OK) - return ret; + result = mbed_set_ssl_version_min_max(data, backend, conn_config); + if(result) + return result; #if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, &rng.drbg); #endif - ret = mbedtls_ssl_setup(&backend->ssl, &backend->config); + ret = mbedtls_ssl_setup(&backend->ssl, + &backend->config); if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedTLS: ssl_setup failed: (-0x%04X) %s", - -ret, errorbuf); + failf(data, "mbedTLS: ssl_setup failed: " + "(-0x%04X) %s", -ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -816,16 +861,15 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, #ifndef MBEDTLS_SSL_PROTO_TLS1_3 if(conn_config->cipher_list) { - CURLcode result = mbed_set_selected_ciphers(data, backend, - conn_config->cipher_list, - NULL); + result = mbed_set_selected_ciphers(data, backend, + conn_config->cipher_list, NULL); #else if(conn_config->cipher_list || conn_config->cipher_list13) { - CURLcode result = mbed_set_selected_ciphers(data, backend, - conn_config->cipher_list, - conn_config->cipher_list13); + result = mbed_set_selected_ciphers(data, backend, + conn_config->cipher_list, + conn_config->cipher_list13); #endif - if(result != CURLE_OK) { + if(result) { failf(data, "mbedTLS: failed to set cipher suites"); return result; } @@ -848,11 +892,9 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, /* Check if there is a cached ID we can/should use here! */ if(Curl_ssl_scache_use(cf, data)) { struct Curl_ssl_session *sc_session = NULL; - CURLcode result; - - result = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key, - &sc_session); - if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) { + CURLcode sresult = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key, + &sc_session); + if(!sresult && sc_session && sc_session->sdata && sc_session->sdata_len) { mbedtls_ssl_session session; mbedtls_ssl_session_init(&session); @@ -870,20 +912,21 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, } mbedtls_ssl_session_free(&session); } - Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session); + Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, + sc_session); } - mbedtls_ssl_conf_ca_chain(&backend->config, - &backend->cacert, + mbedtls_ssl_conf_ca_chain(&backend->config, &backend->cacert, #ifdef MBEDTLS_X509_CRL_PARSE_C - &backend->crl); + &backend->crl #else - NULL); + NULL #endif + ); if(ssl_config->key || ssl_config->key_blob) { - mbedtls_ssl_conf_own_cert(&backend->config, - &backend->clicert, &backend->pk); + mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert, + &backend->pk); } if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ? @@ -917,30 +960,59 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, /* give application a chance to interfere with mbedTLS set up. */ if(data->set.ssl.fsslctx) { - CURLcode result = (*data->set.ssl.fsslctx)(data, &backend->config, - data->set.ssl.fsslctxp); - if(result != CURLE_OK) { + result = (*data->set.ssl.fsslctx)(data, &backend->config, + data->set.ssl.fsslctxp); + if(result) failf(data, "error signaled by ssl ctx callback"); - return result; - } } - connssl->connecting_state = ssl_connect_2; + return result; +} +static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + CURLcode result; + + if((conn_config->version == CURL_SSLVERSION_SSLv2) || + (conn_config->version == CURL_SSLVERSION_SSLv3)) { + failf(data, "Not supported SSL version"); + return CURLE_NOT_BUILT_IN; + } + + result = mbed_load_cacert(cf, data); + if(!result) + result = mbed_load_clicert(cf, data); + if(!result) + result = mbed_load_privkey(cf, data); + if(!result) + result = mbed_load_crl(cf, data); + if(!result) + result = mbed_configure_ssl(cf, data); + if(result) + return result; + + connssl->connecting_state = ssl_connect_2; return CURLE_OK; } +#if defined(MBEDTLS_PK_WRITE_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +#define HAVE_PINNED_PUBKEY +#endif + static CURLcode mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { -#if defined(MBEDTLS_PK_WRITE_C) || defined(HAS_ALPN_MBEDTLS) +#if defined(HAVE_PINNED_PUBKEY) || defined(HAS_ALPN_MBEDTLS) CURLcode result; #endif int ret; struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; -#ifdef MBEDTLS_PK_WRITE_C +#ifdef HAVE_PINNED_PUBKEY #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : @@ -986,7 +1058,7 @@ static CURLcode mbed_connect_step2(struct Curl_cfilter *cf, mbedtls_ssl_get_version(&backend->ssl), cipher_str); } -#ifdef MBEDTLS_PK_WRITE_C +#ifdef HAVE_PINNED_PUBKEY if(pinnedpubkey) { int size; const mbedtls_x509_crt *peercert; @@ -1133,7 +1205,6 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result = CURLE_OK; int nwritten; - (void)data; DEBUGASSERT(backend); *pnwritten = 0; connssl->io_need = CURL_SSL_IO_NEED_NONE; @@ -1305,7 +1376,6 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result = CURLE_OK; int nread; - (void)data; DEBUGASSERT(backend); *pnread = 0; connssl->io_need = CURL_SSL_IO_NEED_NONE; @@ -1372,7 +1442,7 @@ static CURLcode mbedtls_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - CURLcode retcode; + CURLcode result; struct ssl_connect_data *connssl = cf->ctx; /* check if the connection has already been established */ @@ -1385,15 +1455,15 @@ static CURLcode mbedtls_connect(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_NONE; if(ssl_connect_1 == connssl->connecting_state) { - retcode = mbed_connect_step1(cf, data); - if(retcode) - return retcode; + result = mbed_connect_step1(cf, data); + if(result) + return result; } if(ssl_connect_2 == connssl->connecting_state) { - retcode = mbed_connect_step2(cf, data); - if(retcode) - return retcode; + result = mbed_connect_step2(cf, data); + if(result) + return result; } if(ssl_connect_3 == connssl->connecting_state) { @@ -1404,9 +1474,9 @@ static CURLcode mbedtls_connect(struct Curl_cfilter *cf, if(mbedtls_ssl_get_version_number(&backend->ssl) <= MBEDTLS_SSL_VERSION_TLS1_2) { - retcode = mbed_new_session(cf, data); - if(retcode) - return retcode; + result = mbed_new_session(cf, data); + if(result) + return result; } connssl->connecting_state = ssl_connect_done; } @@ -1442,7 +1512,7 @@ static int mbedtls_init(void) NULL, 0); if(ret) { - failf(NULL, " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", + failf(NULL, "failed: mbedtls_ctr_drbg_seed returned -0x%x", (unsigned int)-ret); return 0; } @@ -1516,13 +1586,19 @@ const struct Curl_ssl Curl_ssl_mbedtls = { SSLSUPP_CA_PATH | SSLSUPP_CAINFO_BLOB | SSLSUPP_CERTINFO | +#ifdef HAVE_PINNED_PUBKEY SSLSUPP_PINNEDPUBKEY | +#endif SSLSUPP_SSL_CTX | #ifdef MBEDTLS_SSL_PROTO_TLS1_3 /* requires mbedTLS 3.6.0+ */ SSLSUPP_TLS13_CIPHERSUITES | #endif SSLSUPP_HTTPS_PROXY | - SSLSUPP_CIPHER_LIST, + SSLSUPP_CIPHER_LIST | +#ifdef MBEDTLS_X509_CRL_PARSE_C + SSLSUPP_CRLFILE | +#endif + 0, sizeof(struct mbed_ssl_backend_data), diff --git a/vendor/curl/lib/vtls/mbedtls.h b/vendor/curl/lib/vtls/mbedtls.h index 3876fe3..d8a0a06 100644 --- a/vendor/curl/lib/vtls/mbedtls.h +++ b/vendor/curl/lib/vtls/mbedtls.h @@ -24,7 +24,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_MBEDTLS diff --git a/vendor/curl/lib/vtls/openssl.c b/vendor/curl/lib/vtls/openssl.c index 84e6eaa..50bf1e0 100644 --- a/vendor/curl/lib/vtls/openssl.c +++ b/vendor/curl/lib/vtls/openssl.c @@ -25,14 +25,35 @@ * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code * but vtls.c should ever call or use these functions. */ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_QUICHE) || defined(USE_OPENSSL) -/* Wincrypt must be included before anything that could include OpenSSL. */ #ifdef USE_WIN32_CRYPTO #include -/* Undefine wincrypt conflicting symbols for BoringSSL. */ +/* If is included directly, or indirectly via , + * , , or something else, does this: + * #define X509_NAME ((LPCSTR)7) + * + * And in BoringSSL/AWC-LC's there is: + * typedef struct X509_name_st X509_NAME; + * etc. + * + * The redefined symbols break these OpenSSL headers when included after + * . + * The workaround is to undefine those defines here (and only here). + * + * For unity builds it may need to be repeated elsewhere too, e.g. in ldap.c, + * to apply to other sources using OpenSSL includes. Each compilation unit + * needs undefine them between the first include and the first + * OpenSSL include. + * + * OpenSSL does this in and , but it + * also does the #undef by including . <3.1.0 only does + * it on the first include. + * + * LibreSSL automatically undefines these symbols before using them. + */ #undef X509_NAME #undef X509_EXTENSIONS #undef PKCS7_ISSUER_AND_SERIAL @@ -41,27 +62,28 @@ #undef OCSP_RESPONSE #endif -#include "../urldata.h" -#include "../curl_trc.h" -#include "../formdata.h" /* for the boundary function */ -#include "../url.h" /* for the ssl config check function */ -#include "../curlx/inet_pton.h" -#include "openssl.h" -#include "../connect.h" -#include "../progress.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "../vauth/vauth.h" -#include "keylog.h" -#include "hostcheck.h" -#include "../transfer.h" -#include "../multiif.h" -#include "../curlx/strerr.h" -#include "../curlx/strparse.h" -#include "../curlx/strcopy.h" -#include "../strdup.h" -#include "apple.h" +#include "urldata.h" +#include "curl_trc.h" +#include "httpsrr.h" +#include "formdata.h" /* for the boundary function */ +#include "url.h" /* for the ssl config check function */ +#include "curlx/inet_pton.h" +#include "vtls/openssl.h" +#include "connect.h" +#include "progress.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vauth/vauth.h" +#include "vtls/keylog.h" +#include "vtls/hostcheck.h" +#include "transfer.h" +#include "multiif.h" +#include "curlx/strerr.h" +#include "curlx/strparse.h" +#include "curlx/strcopy.h" +#include "curlx/strdup.h" +#include "vtls/apple.h" #include #include @@ -107,8 +129,6 @@ #include /* this is used in the following conditions to make them easier to read */ #define OPENSSL_HAS_PROVIDERS - -static void ossl_provider_cleanup(struct Curl_easy *data); #endif /* AWS-LC fixed a bug with large buffers in v1.61.0 which also introduced @@ -181,8 +201,6 @@ typedef unsigned long sslerr_t; #endif #define ossl_valsize_t numcert_t -static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl); - static CURLcode push_certinfo(struct Curl_easy *data, BIO *mem, const char *label, int num) WARN_UNUSED_RESULT; @@ -246,7 +264,7 @@ static CURLcode X509V3_ext(struct Curl_easy *data, return result; for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) { - ASN1_OBJECT *obj; + const ASN1_OBJECT *obj; X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, (ossl_valsize_t)i); BUF_MEM *biomem; char namebuf[128]; @@ -262,7 +280,8 @@ static CURLcode X509V3_ext(struct Curl_easy *data, namebuf[sizeof(namebuf) - 1] = 0; if(!X509V3_EXT_print(bio_out, ext, 0, 0)) - ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); + ASN1_STRING_print(bio_out, + (const ASN1_STRING *)X509_EXTENSION_get_data(ext)); BIO_get_mem_ptr(bio_out, &biomem); result = Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data, @@ -371,6 +390,13 @@ static CURLcode get_pkey_dh(struct Curl_easy *data, return result; } +#ifdef HAVE_OPENSSL3 +/* from OpenSSL commit fc756e594ed5a27af378 */ +typedef const X509_PUBKEY pubkeytype_t; +#else +typedef X509_PUBKEY pubkeytype_t; +#endif + static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) { CURLcode result; @@ -435,7 +461,7 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) { const X509_ALGOR *sigalg = NULL; - X509_PUBKEY *xpubkey = NULL; + pubkeytype_t *xpubkey = NULL; ASN1_OBJECT *pubkeyoid = NULL; X509_get0_signature(&psig, &sigalg, x); @@ -592,7 +618,7 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) BIO_set_retry_write(bio); return -1; } @@ -621,7 +647,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) BIO_set_retry_read(bio); } else { @@ -664,6 +690,7 @@ static void ossl_bio_cf_method_free(BIO_METHOD *m) BIO_meth_free(m); } +#ifndef HAVE_KEYLOG_UPSTREAM #ifdef HAVE_KEYLOG_CALLBACK static void ossl_keylog_callback(const SSL *ssl, const char *line) { @@ -709,6 +736,7 @@ static void ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) master_key, master_key_length); } #endif /* !HAVE_KEYLOG_CALLBACK */ +#endif /* HAVE_KEYLOG_UPSTREAM */ static const char *SSL_ERROR_to_str(int err) { @@ -897,10 +925,9 @@ static bool is_pkcs11_uri(const char *string) #endif -static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine); +static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name); #ifdef OPENSSL_HAS_PROVIDERS -static CURLcode ossl_set_provider(struct Curl_easy *data, - const char *provider); +static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname); #endif static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob, @@ -1025,8 +1052,8 @@ static int enginecheck(struct Curl_easy *data, SSL_CTX* ctx, const char *key_file, const char *key_passwd) -#ifdef USE_OPENSSL_ENGINE { +#ifdef USE_OPENSSL_ENGINE EVP_PKEY *priv_key = NULL; /* Implicitly use pkcs11 engine if none was provided and the @@ -1069,22 +1096,20 @@ static int enginecheck(struct Curl_easy *data, return 0; } return 1; -} #else -{ (void)ctx; (void)key_file; (void)key_passwd; failf(data, "SSL_FILETYPE_ENGINE not supported for private key"); return 0; -} #endif +} static int providercheck(struct Curl_easy *data, SSL_CTX* ctx, const char *key_file) -#ifdef OPENSSL_HAS_PROVIDERS { +#ifdef OPENSSL_HAS_PROVIDERS char error_buffer[256]; /* Implicitly use pkcs11 provider if none was provided and the * key_file is a PKCS#11 URI */ @@ -1158,22 +1183,20 @@ static int providercheck(struct Curl_easy *data, return 0; } return 1; -} #else -{ (void)ctx; (void)key_file; failf(data, "SSL_FILETYPE_PROVIDER not supported for private key"); return 0; -} #endif +} static int engineload(struct Curl_easy *data, SSL_CTX* ctx, const char *cert_file) +{ /* ENGINE_CTRL_GET_CMD_FROM_NAME supported by OpenSSL, LibreSSL <=3.8.3 */ #if defined(USE_OPENSSL_ENGINE) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME) -{ char error_buffer[256]; /* Implicitly use pkcs11 engine if none was provided and the * cert_file is a PKCS#11 URI */ @@ -1203,8 +1226,7 @@ static int engineload(struct Curl_easy *data, } /* Load the certificate from the engine */ - if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, - 0, ¶ms, NULL, 1)) { + if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, 0, ¶ms, NULL, 1)) { failf(data, "ssl engine cannot load client cert with id '%s' [%s]", cert_file, ossl_strerror(ERR_get_error(), error_buffer, @@ -1231,21 +1253,19 @@ static int engineload(struct Curl_easy *data, return 0; } return 1; -} #else -{ (void)ctx; (void)cert_file; failf(data, "SSL_FILETYPE_ENGINE not supported for certificate"); return 0; -} #endif +} static int providerload(struct Curl_easy *data, SSL_CTX* ctx, const char *cert_file) -#ifdef OPENSSL_HAS_PROVIDERS { +#ifdef OPENSSL_HAS_PROVIDERS char error_buffer[256]; /* Implicitly use pkcs11 provider if none was provided and the * cert_file is a PKCS#11 URI */ @@ -1309,15 +1329,13 @@ static int providerload(struct Curl_easy *data, return 0; } return 1; -} #else -{ (void)ctx; (void)cert_file; failf(data, "SSL_FILETYPE_PROVIDER not supported for certificate"); return 0; -} #endif +} static int pkcs12load(struct Curl_easy *data, SSL_CTX* ctx, @@ -1337,7 +1355,7 @@ static int pkcs12load(struct Curl_easy *data, if(!cert_bio) { failf(data, "BIO_new_mem_buf NULL, " OSSL_PACKAGE " error %s", ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); + sizeof(error_buffer))); return 0; } } @@ -1346,7 +1364,7 @@ static int pkcs12load(struct Curl_easy *data, if(!cert_bio) { failf(data, "BIO_new return NULL, " OSSL_PACKAGE " error %s", ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); + sizeof(error_buffer))); return 0; } @@ -1521,7 +1539,7 @@ static CURLcode client_cert(struct Curl_easy *data, return CURLE_BAD_FUNCTION_ARGUMENT; } - if((!key_file) && (!key_blob)) { + if(!key_file && !key_blob) { key_file = cert_file; key_blob = cert_blob; } @@ -1607,9 +1625,9 @@ static CURLcode client_cert(struct Curl_easy *data, return CURLE_OK; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE /* returns non-zero on failure */ -static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d) +static CURLcode x509_name_oneline(const X509_NAME *a, struct dynbuf *d) { BIO *bio_out = BIO_new(BIO_s_mem()); BUF_MEM *biomem; @@ -1652,7 +1670,9 @@ static int ossl_init(void) 0; OPENSSL_init_ssl(flags, NULL); +#ifndef HAVE_KEYLOG_UPSTREAM Curl_tls_keylog_open(); +#endif return 1; } @@ -1660,7 +1680,9 @@ static int ossl_init(void) /* Global cleanup */ static void ossl_cleanup(void) { +#ifndef HAVE_KEYLOG_UPSTREAM Curl_tls_keylog_close(); +#endif } /* Selects an OpenSSL crypto engine or provider. @@ -1856,7 +1878,6 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; char buf[1024]; int nread = -1, err; - unsigned long sslerr; size_t i; DEBUGASSERT(octx); @@ -1935,7 +1956,7 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "SSL shutdown not received, but closed"); *done = TRUE; break; - case SSL_ERROR_NONE: /* just did not get anything */ + case SSL_ERROR_NONE: /* did not get anything */ case SSL_ERROR_WANT_READ: /* SSL has send its notify and now wants to read the reply * from the server. We are not really interested in that. */ @@ -1949,12 +1970,14 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, default: /* Server seems to have closed the connection without sending us * a close notify. */ - sslerr = ERR_get_error(); - CURL_TRC_CF(data, cf, "SSL shutdown, ignore recv error: '%s', errno %d", - (sslerr ? - ossl_strerror(sslerr, buf, sizeof(buf)) : - SSL_ERROR_to_str(err)), - SOCKERRNO); + { + VERBOSE(unsigned long sslerr = ERR_get_error()); + CURL_TRC_CF(data, cf, "SSL shutdown, ignore recv error: '%s', errno %d", + (sslerr ? + ossl_strerror(sslerr, buf, sizeof(buf)) : + SSL_ERROR_to_str(err)), + SOCKERRNO); + } *done = TRUE; result = CURLE_OK; break; @@ -2168,7 +2191,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, bool free_cn = FALSE; /* The following is done because of a bug in 0.9.6b */ - X509_NAME *name = X509_get_subject_name(server_cert); + const X509_NAME *name = X509_get_subject_name(server_cert); if(name) { int j; while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) @@ -2180,7 +2203,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, UTF8, etc. */ if(i >= 0) { - ASN1_STRING *tmp = + const ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input @@ -2549,9 +2572,11 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, && content_type != SSL3_RT_INNER_CONTENT_TYPE #endif ) { - const char *msg_name, *tls_rt_name; + const char *msg_name = "Truncated message"; + const char *tls_rt_name; char ssl_buf[1024]; - int msg_type, txt_len; + int msg_type = 0; + int txt_len; /* the info given when the version is zero is not that useful for us */ @@ -2567,15 +2592,21 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, tls_rt_name = ""; if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) { - msg_type = *(const char *)buf; - msg_name = "Change cipher spec"; + if(len) { + msg_type = *(const unsigned char *)buf; + msg_name = "Change cipher spec"; + } } else if(content_type == SSL3_RT_ALERT) { - msg_type = (((const char *)buf)[0] << 8) + ((const char *)buf)[1]; - msg_name = SSL_alert_desc_string_long(msg_type); + if(len >= 2) { + msg_type = + (((const unsigned char *)buf)[0] << 8) + + ((const unsigned char *)buf)[1]; + msg_name = SSL_alert_desc_string_long(msg_type); + } } - else { - msg_type = *(const char *)buf; + else if(len) { + msg_type = *(const unsigned char *)buf; msg_name = ssl_msg_type(ssl_ver, msg_type); } @@ -2591,9 +2622,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, (void)ssl; } -static CURLcode -ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx, - unsigned int ssl_version_min) +static CURLcode ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, + SSL_CTX *ctx, + unsigned int ssl_version_min) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); /* first, TLS min version... */ @@ -2714,7 +2745,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, earlydata_max = SSL_SESSION_get_max_early_data(session); #endif if(quic_tp && quic_tp_len) { - qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len); + qtp_clone = curlx_memdup0((const char *)quic_tp, quic_tp_len); if(!qtp_clone) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -2724,7 +2755,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, result = Curl_ssl_session_create2(der_session_buf, der_session_size, ietf_tls_id, alpn, (curl_off_t)time(NULL) + - SSL_SESSION_get_timeout(session), + SSL_SESSION_get_timeout(session), earlydata_max, qtp_clone, quic_tp_len, &sc_session); der_session_buf = NULL; /* took ownership of sdata */ @@ -2749,8 +2780,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) struct Curl_easy *data = CF_DATA_CURRENT(cf); struct ssl_connect_data *connssl = cf->ctx; Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid, - SSL_version(ssl), connssl->negotiated.alpn, - NULL, 0); + SSL_version(ssl), connssl->negotiated.alpn, NULL, 0); } return 0; } @@ -2762,7 +2792,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, BIO *cbio = NULL; STACK_OF(X509_INFO) *inf = NULL; - /* everything else is just a reference */ + /* everything else is a reference */ int i, count = 0; X509_INFO *itmp = NULL; @@ -2853,7 +2883,7 @@ static CURLcode ossl_win_load_store(struct Curl_easy *data, if(!pContext) break; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) else { char cert_name[256]; if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, @@ -2890,8 +2920,8 @@ static CURLcode ossl_win_load_store(struct Curl_easy *data, * depending on what is found. For more details see * CertGetEnhancedKeyUsage doc. */ - if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { - if(req_size && req_size > enhkey_usage_size) { + if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size) && req_size) { + if(req_size > enhkey_usage_size) { void *tmp = curlx_realloc(enhkey_usage, req_size); if(!tmp) { @@ -2942,7 +2972,7 @@ static CURLcode ossl_win_load_store(struct Curl_easy *data, such as duplicate certificate, which is allowed by MS but not OpenSSL. */ if(X509_STORE_add_cert(store, x509) == 1) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#ifdef DEBUGBUILD infof(data, "SSL: Imported cert"); #endif *padded = TRUE; @@ -3141,7 +3171,7 @@ static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf, * revocation */ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); if(!lookup || - (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { + (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM))) { failf(data, "error loading CRL file: %s", ssl_crlfile); return CURLE_SSL_CRL_BADFILE; } @@ -3243,7 +3273,7 @@ static X509_STORE *ossl_get_cached_x509_store(struct Curl_cfilter *cf, !ossl_cached_x509_store_expired(data, share) && !ossl_cached_x509_store_different(cf, data, share)) { store = share->store; - *pempty = share->store_is_empty; + *pempty = (bool)share->store_is_empty; } return store; @@ -3336,7 +3366,7 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, result = ossl_populate_x509_store(cf, data, octx, store); if(result == CURLE_OK && cache_criteria_met) { - ossl_set_cached_x509_store(cf, data, store, octx->store_is_empty); + ossl_set_cached_x509_store(cf, data, store, (bool)octx->store_is_empty); } } @@ -3356,7 +3386,6 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx, struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_cfg = Curl_ssl_cf_get_primary_config(cf); struct alpn_spec alpns; - char error_buffer[256]; CURLcode result; Curl_alpn_copy(&alpns, alpns_requested); @@ -3372,43 +3401,54 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx, SSL_SESSION *ssl_session = NULL; /* If OpenSSL does not accept the session from the cache, this - * is not an error. We just continue without it. */ + * is not an error. We continue without it. */ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid, (long)der_sessionid_size); if(ssl_session) { if(!SSL_set_session(octx->ssl, ssl_session)) { + VERBOSE(char error_buffer[256]); infof(data, "SSL: SSL_set_session not accepted, " "continuing without: %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); } else { - infof(data, "SSL reusing session with ALPN '%s'", - scs->alpn ? scs->alpn : "-"); - octx->reused_session = TRUE; + if(conn_cfg->verifypeer && + (SSL_get_verify_result(octx->ssl) != X509_V_OK)) { + /* Session was from unverified connection, cannot reuse here */ + SSL_set_session(octx->ssl, NULL); + infof(data, "SSL session not peer verified, not reusing"); + } + else { + infof(data, "SSL reusing session with ALPN '%s'", + scs->alpn ? scs->alpn : "-"); + octx->reused_session = TRUE; + infof(data, "SSL verify result: %lx", + SSL_get_verify_result(octx->ssl)); #ifdef HAVE_OPENSSL_EARLYDATA - if(ssl_config->earlydata && scs->alpn && - SSL_SESSION_get_max_early_data(ssl_session) && - !cf->conn->connect_only && - (SSL_version(octx->ssl) == TLS1_3_VERSION)) { - bool do_early_data = FALSE; - if(sess_reuse_cb) { - result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); - if(result) { - SSL_SESSION_free(ssl_session); - return result; + if(ssl_config->earlydata && scs->alpn && + SSL_SESSION_get_max_early_data(ssl_session) && + !cf->conn->connect_only && + (SSL_version(octx->ssl) == TLS1_3_VERSION)) { + bool do_early_data = FALSE; + if(sess_reuse_cb) { + result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); + if(result) { + SSL_SESSION_free(ssl_session); + return result; + } + } + if(do_early_data) { + /* We only try the ALPN protocol the session used before, + * otherwise we might send early data for the wrong protocol */ + Curl_alpn_restrict_to(&alpns, scs->alpn); } } - if(do_early_data) { - /* We only try the ALPN protocol the session used before, - * otherwise we might send early data for the wrong protocol */ - Curl_alpn_restrict_to(&alpns, scs->alpn); - } - } #else - (void)ssl_config; - (void)sess_reuse_cb; + (void)ssl_config; + (void)sess_reuse_cb; #endif + } } SSL_SESSION_free(ssl_session); } @@ -3427,7 +3467,7 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx, failf(data, "Error determining ALPN"); return CURLE_SSL_CONNECT_ERROR; } - if(SSL_set_alpn_protos(octx->ssl, proto.data, (int)proto.len)) { + if(SSL_set_alpn_protos(octx->ssl, proto.data, proto.len)) { failf(data, "Error setting ALPN"); return CURLE_SSL_CONNECT_ERROR; } @@ -3453,14 +3493,14 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, if(data->set.tls_ech & CURLECH_GREASE) { infof(data, "ECH: will GREASE ClientHello"); -# ifdef HAVE_BORINGSSL_LIKE +#ifdef HAVE_BORINGSSL_LIKE SSL_set_enable_ech_grease(octx->ssl, 1); -# else +#else SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); -# endif +#endif } else if(data->set.tls_ech & CURLECH_CLA_CFG) { -# ifdef HAVE_BORINGSSL_LIKE +#ifdef HAVE_BORINGSSL_LIKE /* have to do base64 decode here for BoringSSL */ const char *b64 = data->set.str[STRING_ECH_CONFIG]; @@ -3484,7 +3524,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } curlx_free(ech_config); trying_ech_now = 1; -# else +#else ech_config = (unsigned char *)data->set.str[STRING_ECH_CONFIG]; if(!ech_config) { infof(data, "ECH: ECHConfig from command line empty"); @@ -3498,7 +3538,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } else trying_ech_now = 1; -# endif +#endif /* HAVE_BORINGSSL_LIKE */ infof(data, "ECH: ECHConfig from command line"); } else { @@ -3539,12 +3579,12 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, Curl_resolv_unlink(data, &dns); } } -# ifdef HAVE_BORINGSSL_LIKE +#ifdef HAVE_BORINGSSL_LIKE if(trying_ech_now && outername) { infof(data, "ECH: setting public_name not supported with BoringSSL"); return CURLE_SSL_CONNECT_ERROR; } -# else +#else if(trying_ech_now && outername) { infof(data, "ECH: inner: '%s', outer: '%s'", peer->hostname ? peer->hostname : "NULL", outername); @@ -3556,7 +3596,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, return CURLE_SSL_CONNECT_ERROR; } } -# endif /* HAVE_BORINGSSL_LIKE */ +#endif /* HAVE_BORINGSSL_LIKE */ if(trying_ech_now && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) { infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); @@ -3622,11 +3662,11 @@ static CURLcode ossl_init_method(struct Curl_cfilter *cf, *pmethod = NULL; *pssl_version_min = conn_config->version; + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); switch(peer->transport) { case TRNSPRT_TCP: /* check to see if we have been told to use an explicit SSL/TLS version */ switch(*pssl_version_min) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -3655,11 +3695,7 @@ static CURLcode ossl_init_method(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } -#ifdef USE_OPENSSL_QUIC - *pmethod = OSSL_QUIC_client_method(); -#else *pmethod = TLS_method(); -#endif break; default: failf(data, "unsupported transport %d in SSL init", peer->transport); @@ -3743,8 +3779,8 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, The enabled extension concerns the session management. I wonder how often libcurl stops a connection and then resumes a TLS session. Also, sending - the session data is some overhead. I suggest that you just use your - proposed patch (which explicitly disables TICKET). + the session data is some overhead. I suggest that you use your proposed + patch (which explicitly disables TICKET). If someone writes an application with libcurl and OpenSSL who wants to enable the feature, one can do this in the SSL callback. @@ -3776,13 +3812,13 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, if(!ssl_config->enable_beast) ctx_options &= ~(ctx_option_t)SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + DEBUGASSERT(ssl_version_min != CURL_SSLVERSION_DEFAULT); switch(ssl_version_min) { case CURL_SSLVERSION_SSLv2: case CURL_SSLVERSION_SSLv3: return CURLE_NOT_BUILT_IN; /* "--tlsv" options mean TLS >= version */ - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */ case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */ case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */ @@ -3926,7 +3962,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, SSL_CTX_set_verify(octx->ssl_ctx, SSL_VERIFY_NONE, NULL); /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ -#ifdef HAVE_KEYLOG_CALLBACK +#if !defined(HAVE_KEYLOG_UPSTREAM) && defined(HAVE_KEYLOG_CALLBACK) if(Curl_tls_keylog_enabled()) { SSL_CTX_set_keylog_callback(octx->ssl_ctx, ossl_keylog_callback); } @@ -3975,32 +4011,16 @@ static CURLcode ossl_on_session_reuse(struct Curl_cfilter *cf, bool *do_early_data) { struct ssl_connect_data *connssl = cf->ctx; - CURLcode result = CURLE_OK; - *do_early_data = FALSE; connssl->earlydata_max = scs->earlydata_max; - if(!connssl->earlydata_max) { - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); - } - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); - connssl->earlydata_state = ssl_earlydata_await; - connssl->state = ssl_connection_deferred; - result = Curl_alpn_set_negotiated(cf, data, connssl, - (const unsigned char *)scs->alpn, - scs->alpn ? strlen(scs->alpn) : 0); - *do_early_data = !result; - } - return result; + + return Curl_on_session_reuse(cf, data, alpns, scs, do_early_data, + connssl->earlydata_max); } void Curl_ossl_report_handshake(struct Curl_easy *data, struct ossl_ctx *octx) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { int psigtype_nid = NID_undef; const char *negotiated_group_name = NULL; @@ -4025,7 +4045,7 @@ void Curl_ossl_report_handshake(struct Curl_easy *data, struct ossl_ctx *octx) #else (void)data; (void)octx; -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ } static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, @@ -4086,27 +4106,27 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl, CURLcode result = CURLE_OK; size_t rcl = 0; int rv = 1; -# ifndef HAVE_BORINGSSL_LIKE +#ifndef HAVE_BORINGSSL_LIKE char *inner = NULL; uint8_t *rcs = NULL; char *outer = NULL; -# else +#else const char *inner = NULL; const uint8_t *rcs = NULL; const char *outer = NULL; size_t out_name_len = 0; int servername_type = 0; -# endif +#endif /* nothing to trace if not doing ECH */ if(!ECH_ENABLED(data)) return; -# ifndef HAVE_BORINGSSL_LIKE +#ifndef HAVE_BORINGSSL_LIKE rv = SSL_ech_get1_retry_config(ssl, &rcs, &rcl); -# else +#else SSL_get0_ech_retry_configs(ssl, &rcs, &rcl); rv = (int)rcl; -# endif +#endif if(rv && rcs) { char *b64str = NULL; @@ -4132,9 +4152,9 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl, } else infof(data, "ECH: no retry_configs (rv = %d)", rv); -# ifndef HAVE_BORINGSSL_LIKE +#ifndef HAVE_BORINGSSL_LIKE OPENSSL_free((void *)rcs); -# endif +#endif return; } @@ -4164,7 +4184,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, octx->x509_store_setup = TRUE; } -#ifndef HAVE_KEYLOG_CALLBACK +#if !defined(HAVE_KEYLOG_UPSTREAM) && !defined(HAVE_KEYLOG_CALLBACK) /* If key logging is enabled, wait for the handshake to complete and then * proceed with logging secrets (for TLS 1.2 or older). */ @@ -4251,11 +4271,11 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, #endif #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST else if((lib == ERR_LIB_SSL) && -# ifndef HAVE_BORINGSSL_LIKE +#ifndef HAVE_BORINGSSL_LIKE (reason == SSL_R_ECH_REQUIRED)) { -# else +#else (reason == SSL_R_ECH_REJECTED)) { -# endif +#endif /* HAVE_BORINGSSL_LIKE */ /* trace retry_configs if we got some */ ossl_trace_ech_retry_configs(data, octx->ssl, reason); @@ -4277,7 +4297,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, * (RST connection, etc.), OpenSSL gives no explanation whatsoever and * the SO_ERROR is also lost. */ - if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { + if(result == CURLE_SSL_CONNECT_ERROR && errdetail == 0) { char extramsg[80] = ""; int sockerr = SOCKERRNO; @@ -4328,9 +4348,18 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, case SSL_ECH_STATUS_BAD_CALL: status = "bad call (unexpected)"; break; - case SSL_ECH_STATUS_BAD_NAME: - status = "bad name (unexpected)"; + case SSL_ECH_STATUS_BAD_NAME: { + struct ssl_primary_config *conn_config = + Curl_ssl_cf_get_primary_config(cf); + if(!conn_config->verifypeer && !conn_config->verifyhost && + inner && !strcmp(inner, connssl->peer.hostname)) { + status = "bad name (tolerated without peer verification)"; + rv = SSL_ECH_STATUS_SUCCESS; + } + else + status = "bad name (unexpected)"; break; + } default: status = "unexpected status"; infof(data, "ECH: unexpected status %d", rv); @@ -4345,8 +4374,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, /* trace retry_configs if we got some */ ossl_trace_ech_retry_configs(data, octx->ssl, 0); } - if(rv != SSL_ECH_STATUS_SUCCESS - && data->set.tls_ech & CURLECH_HARD) { + if(rv != SSL_ECH_STATUS_SUCCESS && (data->set.tls_ech & CURLECH_HARD)) { infof(data, "ECH: ech-hard failed"); return CURLE_SSL_CONNECT_ERROR; } @@ -4426,9 +4454,9 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509 *cert, return result; } -#if !(defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER < 0x3060000fL) && \ - !defined(HAVE_BORINGSSL_LIKE) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#ifdef CURLVERBOSE +#if !defined(HAVE_BORINGSSL_LIKE) && \ + !(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3060000fL) static void infof_certstack(struct Curl_easy *data, const SSL *ssl) { STACK_OF(X509) *certstack; @@ -4501,6 +4529,7 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl) #else #define infof_certstack(data, ssl) #endif +#endif /* CURLVERBOSE */ static CURLcode ossl_check_issuer(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -4595,7 +4624,7 @@ static CURLcode ossl_check_pinned_key(struct Curl_cfilter *cf, return result; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE #define MAX_CERT_NAME_LENGTH 2048 static CURLcode ossl_infof_cert(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -4646,7 +4675,7 @@ static CURLcode ossl_infof_cert(struct Curl_cfilter *cf, curlx_dyn_free(&dname); return result; } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ #ifdef USE_APPLE_SECTRUST struct ossl_certs_ctx { @@ -4698,8 +4727,15 @@ static CURLcode ossl_apple_verify(struct Curl_cfilter *cf, if(!chain.num_certs && (conn_config->verifypeer || conn_config->verifyhost)) { - failf(data, "SSL: could not get peer certificate"); - result = CURLE_PEER_FAILED_VERIFICATION; + if(!octx->reused_session) { + failf(data, "SSL: could not get peer certificate chain"); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else { + /* when session was reused, there is no peer cert chain */ + *pverified = FALSE; + return CURLE_OK; + } } else { #ifdef HAVE_BORINGSSL_LIKE @@ -4759,7 +4795,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, goto out; } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE result = ossl_infof_cert(cf, data, server_cert); if(result) goto out; @@ -4775,6 +4811,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, ossl_verify = SSL_get_verify_result(octx->ssl); ssl_config->certverifyresult = ossl_verify; + infof(data, "OpenSSL verify result: %lx", ossl_verify); verified = (ossl_verify == X509_V_OK); if(verified) @@ -5020,7 +5057,7 @@ static bool ossl_data_pending(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; (void)data; - return connssl->input_pending; + return (bool)connssl->input_pending; } static CURLcode ossl_send(struct Curl_cfilter *cf, @@ -5040,7 +5077,6 @@ static CURLcode ossl_send(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; int nwritten; - (void)data; DEBUGASSERT(octx); *pnwritten = 0; ERR_clear_error(); @@ -5133,7 +5169,6 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; int nread; - (void)data; DEBUGASSERT(octx); *pnread = 0; @@ -5147,7 +5182,7 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, *pnread = (size_t)nread; else { /* failed SSL_read */ - int err = SSL_get_error(octx->ssl, (int)nread); + int err = SSL_get_error(octx->ssl, nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -5359,7 +5394,7 @@ static CURLcode ossl_random(struct Curl_easy *data, if(!rand_enough()) return CURLE_FAILED_INIT; } - /* RAND_bytes() returns 1 on success, 0 otherwise. */ + /* RAND_bytes() returns 1 on success, 0 otherwise. */ rc = RAND_bytes(entropy, (ossl_valsize_t)curlx_uztosi(length)); return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT; } @@ -5424,7 +5459,11 @@ const struct Curl_ssl Curl_ssl_openssl = { #endif SSLSUPP_CA_CACHE | SSLSUPP_HTTPS_PROXY | - SSLSUPP_CIPHER_LIST, + SSLSUPP_CIPHER_LIST | + SSLSUPP_ISSUERCERT | + SSLSUPP_ISSUERCERT_BLOB | + SSLSUPP_SSL_EC_CURVES | + SSLSUPP_CRLFILE, sizeof(struct ossl_ctx), diff --git a/vendor/curl/lib/vtls/openssl.h b/vendor/curl/lib/vtls/openssl.h index aeeb8dd..cfffe9d 100644 --- a/vendor/curl/lib/vtls/openssl.h +++ b/vendor/curl/lib/vtls/openssl.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_OPENSSL /* @@ -34,7 +34,7 @@ #include #include -#include "../urldata.h" +#include "urldata.h" #if OPENSSL_VERSION_NUMBER >= 0x30000000L #define HAVE_OPENSSL3 /* non-fork OpenSSL 3.x or later */ @@ -44,6 +44,12 @@ #define HAVE_BORINGSSL_LIKE #endif +/* OpenSSL 3.5.0+ has built-in 'SSLKEYLOGFILE' support if built with + 'enable-sslkeylog' */ +#if OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_SSLKEYLOG) +#define HAVE_KEYLOG_UPSTREAM +#endif + /* * Whether SSL_CTX_set_keylog_callback is available. * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 @@ -73,7 +79,7 @@ struct ossl_ctx { CURLcode io_result; /* result of last BIO cfilter operation */ /* blocked writes need to retry with same length, remember it */ int blocked_ssl_write_len; -#ifndef HAVE_KEYLOG_CALLBACK +#if !defined(HAVE_KEYLOG_UPSTREAM) && !defined(HAVE_KEYLOG_CALLBACK) /* Set to true once a valid keylog entry has been created to avoid dupes. This is a bool and not a bitfield because it is passed by address. */ bool keylog_done; @@ -100,7 +106,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - const struct alpn_spec *alpns, + const struct alpn_spec *alpns_requested, Curl_ossl_ctx_setup_cb *cb_setup, void *cb_user_data, Curl_ossl_new_session_cb *cb_new_session, @@ -132,7 +138,7 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf, CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, - SSL_SESSION *ssl_sessionid, + SSL_SESSION *session, int ietf_tls_id, const char *alpn, unsigned char *quic_tp, diff --git a/vendor/curl/lib/vtls/rustls.c b/vendor/curl/lib/vtls/rustls.c index fc380b0..061b444 100644 --- a/vendor/curl/lib/vtls/rustls.c +++ b/vendor/curl/lib/vtls/rustls.c @@ -23,22 +23,23 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_RUSTLS #include -#include "../curlx/fopen.h" -#include "../curlx/strerr.h" -#include "../urldata.h" -#include "../curl_trc.h" -#include "vtls.h" -#include "vtls_int.h" -#include "rustls.h" -#include "keylog.h" -#include "cipher_suite.h" -#include "x509asn1.h" +#include "curlx/fopen.h" +#include "curlx/strerr.h" +#include "urldata.h" +#include "curl_trc.h" +#include "httpsrr.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/rustls.h" +#include "vtls/keylog.h" +#include "vtls/cipher_suite.h" +#include "vtls/x509asn1.h" struct rustls_ssl_backend_data { const struct rustls_client_config *config; @@ -82,7 +83,7 @@ static bool cr_data_pending(struct Curl_cfilter *cf, (void)data; DEBUGASSERT(ctx && ctx->backend); backend = (struct rustls_ssl_backend_data *)ctx->backend; - return backend->data_in_pending; + return (bool)backend->data_in_pending; } struct io_ctx { @@ -104,7 +105,7 @@ static int read_cb(void *userdata, uint8_t *buf, uintptr_t len, if(result) { nread = 0; /* !checksrc! disable ERRNOVAR 4 */ - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) ret = EAGAIN; else ret = EINVAL; @@ -129,7 +130,7 @@ static int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, buf, len, FALSE, &nwritten); if(result) { nwritten = 0; - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) ret = EAGAIN; else ret = EINVAL; @@ -354,7 +355,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, result = cr_flush_out(cf, data, rconn); if(result) { - if(CURLE_AGAIN == result) { + if(result == CURLE_AGAIN) { /* The TLS bytes may have been partially written, but we fail the * complete send() and remember how much we already added to Rustls. */ backend->plain_out_buffered = plainwritten; @@ -532,8 +533,8 @@ init_config_builder(struct Curl_easy *data, CURLcode result = CURLE_OK; rustls_result rr; + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -576,7 +577,7 @@ init_config_builder(struct Curl_easy *data, } #endif /* USE_ECH */ - cipher_suites = curlx_malloc(sizeof(*cipher_suites) * (cipher_suites_len)); + cipher_suites = curlx_malloc(sizeof(*cipher_suites) * cipher_suites_len); if(!cipher_suites) { result = CURLE_OUT_OF_MEMORY; goto cleanup; @@ -1171,6 +1172,7 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, } /* REALLY Done with the handshake. */ { +#ifdef CURLVERBOSE const uint16_t proto = rustls_connection_get_protocol_version(rconn); const rustls_str ciphersuite_name = rustls_connection_get_negotiated_ciphersuite_name(rconn); @@ -1181,6 +1183,7 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, ver = "TLSv1.3"; if(proto == RUSTLS_TLS_VERSION_TLSV1_2) ver = "TLSv1.2"; +#endif infof(data, "rustls: handshake complete, %s, ciphersuite: %.*s, " "key exchange group: %.*s", @@ -1398,7 +1401,8 @@ const struct Curl_ssl Curl_ssl_rustls = { SSLSUPP_CIPHER_LIST | SSLSUPP_TLS13_CIPHERSUITES | SSLSUPP_CERTINFO | - SSLSUPP_ECH, + SSLSUPP_ECH | + SSLSUPP_CRLFILE, sizeof(struct rustls_ssl_backend_data), NULL, /* init */ diff --git a/vendor/curl/lib/vtls/rustls.h b/vendor/curl/lib/vtls/rustls.h index 2d3b0bb..b6ddbd1 100644 --- a/vendor/curl/lib/vtls/rustls.h +++ b/vendor/curl/lib/vtls/rustls.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_RUSTLS diff --git a/vendor/curl/lib/vtls/schannel.c b/vendor/curl/lib/vtls/schannel.c index 891dfc4..650a0e3 100644 --- a/vendor/curl/lib/vtls/schannel.c +++ b/vendor/curl/lib/vtls/schannel.c @@ -27,33 +27,33 @@ * Source file for all Schannel-specific code for the TLS/SSL layer. No code * but vtls.c should ever call or use these functions. */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL #ifndef USE_WINDOWS_SSPI -#error "cannot compile SCHANNEL support without SSPI." +#error "cannot compile Schannel support without SSPI." #endif -#include "schannel.h" -#include "schannel_int.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "../curl_trc.h" -#include "../connect.h" /* for the connect timeout */ -#include "../strdup.h" -#include "../strerror.h" -#include "../select.h" /* for the socket readiness */ -#include "../curlx/fopen.h" -#include "../curlx/multibyte.h" -#include "x509asn1.h" -#include "../system_win32.h" -#include "../curlx/version_win32.h" -#include "../rand.h" -#include "../curlx/strparse.h" -#include "../progress.h" -#include "../curl_sha256.h" +#include "vtls/schannel.h" +#include "vtls/schannel_int.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "curl_trc.h" +#include "connect.h" /* for the connect timeout */ +#include "curlx/strdup.h" +#include "strerror.h" +#include "select.h" /* for the socket readiness */ +#include "curlx/fopen.h" +#include "curlx/multibyte.h" +#include "vtls/x509asn1.h" +#include "system_win32.h" +#include "curlx/version_win32.h" +#include "rand.h" +#include "curlx/strparse.h" +#include "progress.h" +#include "curl_sha256.h" /* Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF() * and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These @@ -105,11 +105,6 @@ * #define failf(x, y, ...) curl_mprintf(y, __VA_ARGS__) */ -/* Offered when targeting Vista (XP SP2+) */ -#ifndef CALG_SHA_256 -#define CALG_SHA_256 0x0000800c -#endif - /* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ #ifndef PKCS12_NO_PERSIST_KEY #define PKCS12_NO_PERSIST_KEY 0x00008000 @@ -134,10 +129,6 @@ static bool s_win_has_alpn; #endif -static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *pinnedpubkey); - static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, void *BufDataPtr, unsigned long BufByteSize) { @@ -209,7 +200,7 @@ static CURLcode schannel_set_ssl_version_min_max(DWORD *enabled_protocols, return CURLE_OK; } -#define CIPHEROPTION(x) {#x, x} +#define CIPHEROPTION(x) { #x, x } struct algo { const char *name; @@ -225,103 +216,52 @@ static const struct algo algs[] = { CIPHEROPTION(CALG_MAC), CIPHEROPTION(CALG_RSA_SIGN), CIPHEROPTION(CALG_DSS_SIGN), -/* ifdefs for the options that are defined conditionally in wincrypt.h */ -#ifdef CALG_NO_SIGN CIPHEROPTION(CALG_NO_SIGN), -#endif CIPHEROPTION(CALG_RSA_KEYX), CIPHEROPTION(CALG_DES), -#ifdef CALG_3DES_112 CIPHEROPTION(CALG_3DES_112), -#endif CIPHEROPTION(CALG_3DES), CIPHEROPTION(CALG_DESX), CIPHEROPTION(CALG_RC2), CIPHEROPTION(CALG_RC4), CIPHEROPTION(CALG_SEAL), -#ifdef CALG_DH_SF CIPHEROPTION(CALG_DH_SF), -#endif CIPHEROPTION(CALG_DH_EPHEM), -#ifdef CALG_AGREEDKEY_ANY CIPHEROPTION(CALG_AGREEDKEY_ANY), -#endif -#ifdef CALG_HUGHES_MD5 CIPHEROPTION(CALG_HUGHES_MD5), -#endif CIPHEROPTION(CALG_SKIPJACK), -#ifdef CALG_TEK CIPHEROPTION(CALG_TEK), -#endif CIPHEROPTION(CALG_CYLINK_MEK), /* spellchecker:disable-line */ CIPHEROPTION(CALG_SSL3_SHAMD5), -#ifdef CALG_SSL3_MASTER CIPHEROPTION(CALG_SSL3_MASTER), -#endif -#ifdef CALG_SCHANNEL_MASTER_HASH CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH), -#endif -#ifdef CALG_SCHANNEL_MAC_KEY CIPHEROPTION(CALG_SCHANNEL_MAC_KEY), -#endif -#ifdef CALG_SCHANNEL_ENC_KEY CIPHEROPTION(CALG_SCHANNEL_ENC_KEY), -#endif -#ifdef CALG_PCT1_MASTER CIPHEROPTION(CALG_PCT1_MASTER), -#endif -#ifdef CALG_SSL2_MASTER CIPHEROPTION(CALG_SSL2_MASTER), -#endif -#ifdef CALG_TLS1_MASTER CIPHEROPTION(CALG_TLS1_MASTER), -#endif -#ifdef CALG_RC5 CIPHEROPTION(CALG_RC5), -#endif -#ifdef CALG_HMAC CIPHEROPTION(CALG_HMAC), -#endif -#ifdef CALG_TLS1PRF CIPHEROPTION(CALG_TLS1PRF), -#endif -#ifdef CALG_HASH_REPLACE_OWF CIPHEROPTION(CALG_HASH_REPLACE_OWF), -#endif -#ifdef CALG_AES_128 CIPHEROPTION(CALG_AES_128), -#endif -#ifdef CALG_AES_192 CIPHEROPTION(CALG_AES_192), -#endif -#ifdef CALG_AES_256 CIPHEROPTION(CALG_AES_256), -#endif -#ifdef CALG_AES CIPHEROPTION(CALG_AES), -#endif -#ifdef CALG_SHA_256 CIPHEROPTION(CALG_SHA_256), -#endif -#ifdef CALG_SHA_384 CIPHEROPTION(CALG_SHA_384), -#endif -#ifdef CALG_SHA_512 CIPHEROPTION(CALG_SHA_512), -#endif -#ifdef CALG_ECDH CIPHEROPTION(CALG_ECDH), -#endif +/* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ #ifdef CALG_ECMQV CIPHEROPTION(CALG_ECMQV), #endif -#ifdef CALG_ECDSA CIPHEROPTION(CALG_ECDSA), -#endif +/* Offered by mingw-w64 v7+. MS SDK 7.0A+. */ #ifdef CALG_ECDH_EPHEM CIPHEROPTION(CALG_ECDH_EPHEM), #endif - {NULL, 0}, + { NULL, 0 }, }; static int get_alg_id_by_name(const char *name) @@ -419,101 +359,19 @@ static CURLcode get_cert_location(TCHAR *path, DWORD *store_name, return CURLE_OK; } -static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode get_client_cert(struct Curl_easy *data, + HCERTSTORE *out_cert_store, + PCCERT_CONTEXT *out_cert_context) { - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - - PCCERT_CONTEXT client_certs[1] = { NULL }; + PCCERT_CONTEXT client_cert = NULL; HCERTSTORE client_cert_store = NULL; - SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode result; - - /* setup Schannel API options */ - DWORD flags = 0; - DWORD enabled_protocols = 0; - - struct schannel_ssl_backend_data *backend = - (struct schannel_ssl_backend_data *)(connssl->backend); - - DEBUGASSERT(backend); - - if(conn_config->verifypeer) { - if(backend->use_manual_cred_validation) - flags = SCH_CRED_MANUAL_CRED_VALIDATION; - else - flags = SCH_CRED_AUTO_CRED_VALIDATION; - - if(ssl_config->no_revoke) { - flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; - - DEBUGF(infof(data, "schannel: disabled server certificate revocation " - "checks")); - } - else if(ssl_config->revoke_best_effort) { - flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE | - SCH_CRED_REVOCATION_CHECK_CHAIN; - - DEBUGF(infof(data, "schannel: ignore revocation offline errors")); - } - else { - flags |= SCH_CRED_REVOCATION_CHECK_CHAIN; - - DEBUGF(infof(data, "schannel: checking server certificate revocation")); - } - } - else { - flags = SCH_CRED_MANUAL_CRED_VALIDATION | - SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; - DEBUGF(infof(data, "schannel: disabled server cert revocation checks")); - } - - if(!conn_config->verifyhost) { - flags |= SCH_CRED_NO_SERVERNAME_CHECK; - DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from " - "comparing the supplied target name with the subject " - "names in server certificates.")); - } - - if(!ssl_config->auto_client_cert) { - flags &= ~(DWORD)SCH_CRED_USE_DEFAULT_CREDS; - flags |= SCH_CRED_NO_DEFAULT_CREDS; - infof(data, "schannel: disabled automatic use of client certificate"); - } - else - infof(data, "schannel: enabled automatic use of client certificate"); - - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: { - result = schannel_set_ssl_version_min_max(&enabled_protocols, cf, data); - if(result) - return result; - break; - } - case CURL_SSLVERSION_SSLv3: - case CURL_SSLVERSION_SSLv2: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } + CURLcode result = CURLE_OK; - /* client certificate */ if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { DWORD cert_store_name = 0; TCHAR *cert_store_path = NULL; TCHAR *cert_thumbprint_str = NULL; + TCHAR cert_thumbprint_buf[CERT_THUMBPRINT_STR_LEN + 1]; CRYPT_HASH_BLOB cert_thumbprint; BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; HCERTSTORE cert_store = NULL; @@ -521,20 +379,30 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, void *certdata = NULL; size_t certsize = 0; bool blob = data->set.ssl.primary.cert_blob != NULL; - TCHAR *cert_path = NULL; + if(blob) { certdata = data->set.ssl.primary.cert_blob->data; certsize = data->set.ssl.primary.cert_blob->len; } else { - cert_path = curlx_convert_UTF8_to_tchar( - data->set.ssl.primary.clientcert); + TCHAR *cert_path = + curlx_convert_UTF8_to_tchar(data->set.ssl.primary.clientcert); if(!cert_path) return CURLE_OUT_OF_MEMORY; result = get_cert_location(cert_path, &cert_store_name, &cert_store_path, &cert_thumbprint_str); + /* 'cert_thumbprint_str' points in to the allocated 'cert_path', copy + the data. The string is verified to be CERT_THUMBPRINT_STR_LEN bytes + long within the get_cert_location() function. */ + if(!result && cert_thumbprint_str) { + memcpy(cert_thumbprint_buf, cert_thumbprint_str, + sizeof(cert_thumbprint_buf)); + cert_thumbprint_str = cert_thumbprint_buf; + } + + curlx_free(cert_path); if(result && (data->set.ssl.primary.clientcert[0] != '\0')) fInCert = curlx_fopen(data->set.ssl.primary.clientcert, "rb"); @@ -542,7 +410,6 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, failf(data, "schannel: Failed to get certificate location" " or file for %s", data->set.ssl.primary.clientcert); - curlx_free(cert_path); return result; } } @@ -550,28 +417,25 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, if((fInCert || blob) && data->set.ssl.cert_type && !curl_strequal(data->set.ssl.cert_type, "P12")) { failf(data, "schannel: certificate format compatibility error " - " for %s", + "for %s", blob ? "(memory blob)" : data->set.ssl.primary.clientcert); curlx_free(cert_store_path); - curlx_free(cert_path); if(fInCert) curlx_fclose(fInCert); return CURLE_SSL_CERTPROBLEM; } if(fInCert || blob) { - /* Reading a .P12 or .pfx file, like the example at bottom of + /* Reading a .p12 or .pfx file, like the example at bottom of https://learn.microsoft.com/archive/msdn-technet-forums/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 */ CRYPT_DATA_BLOB datablob; WCHAR *pszPassword; size_t pwd_len = 0; - int str_w_len = 0; int cert_find_flags; const char *cert_showfilename_error = blob ? "(memory blob)" : data->set.ssl.primary.clientcert; curlx_free(cert_store_path); - curlx_free(cert_path); if(fInCert) { long cert_tell = 0; bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; @@ -605,6 +469,7 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, pwd_len = strlen(data->set.ssl.key_passwd); pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); if(pszPassword) { + int str_w_len = 0; if(pwd_len > 0) str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, @@ -617,12 +482,8 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, else pszPassword[0] = 0; - if(Curl_isVistaOrGreater) - cert_store = PFXImportCertStore(&datablob, pszPassword, - PKCS12_NO_PERSIST_KEY); - else - cert_store = PFXImportCertStore(&datablob, pszPassword, 0); - + cert_store = PFXImportCertStore(&datablob, pszPassword, + PKCS12_NO_PERSIST_KEY); curlx_free(pszPassword); } if(!blob) @@ -648,11 +509,12 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, else cert_find_flags = CERT_FIND_ANY; - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - cert_find_flags, NULL, NULL); + client_cert = + CertFindCertificateInStore(cert_store, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + cert_find_flags, NULL, NULL); - if(!client_certs[0]) { + if(!client_cert) { failf(data, "schannel: Failed to get certificate from file %s" ", last error is 0x%08lx", cert_showfilename_error, GetLastError()); @@ -677,12 +539,10 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, curlx_convert_tchar_to_UTF8(cert_store_path); failf(data, "schannel: Failed to open cert store %lx %s, " "last error is 0x%08lx", - cert_store_name, - (path_utf8 ? path_utf8 : "(unknown)"), + cert_store_name, (path_utf8 ? path_utf8 : "(unknown)"), GetLastError()); curlx_free(cert_store_path); curlx_free(path_utf8); - curlx_free(cert_path); return CURLE_SSL_CERTPROBLEM; } curlx_free(cert_store_path); @@ -696,18 +556,15 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, cert_thumbprint_data, &cert_thumbprint.cbData, NULL, NULL)) { - curlx_free(cert_path); CertCloseStore(cert_store, 0); return CURLE_SSL_CERTPROBLEM; } - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_HASH, &cert_thumbprint, NULL); - - curlx_free(cert_path); - - if(!client_certs[0]) { + client_cert = + CertFindCertificateInStore(cert_store, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_HASH, &cert_thumbprint, NULL); + if(!client_cert) { /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ CertCloseStore(cert_store, 0); failf(data, "schannel: client cert not found in cert store"); @@ -717,25 +574,22 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, client_cert_store = cert_store; } - /* allocate memory for the reusable credential handle */ - backend->cred = (struct Curl_schannel_cred *) - curlx_calloc(1, sizeof(struct Curl_schannel_cred)); - if(!backend->cred) { - failf(data, "schannel: unable to allocate memory"); - - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); - if(client_cert_store) - CertCloseStore(client_cert_store, 0); + *out_cert_store = client_cert_store; + *out_cert_context = client_cert; - return CURLE_OUT_OF_MEMORY; - } - backend->cred->refcount = 1; + return CURLE_OK; +} - /* Since we did not persist the key, we need to extend the store's - * lifetime until the end of the connection - */ - backend->cred->client_cert_store = client_cert_store; +static CURLcode acquire_sspi_handle(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct schannel_ssl_backend_data *backend, + PCCERT_CONTEXT client_cert, + DWORD flags, + DWORD enabled_protocols) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + SECURITY_STATUS sspi_status = SEC_E_OK; + CURLcode result; /* We support TLS 1.3 starting in Windows 10 version 1809 (OS build 17763) as long as the user did not set a legacy algorithm list @@ -747,6 +601,12 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, SCH_CREDENTIALS credentials = { 0 }; TLS_PARAMETERS tls_parameters = { 0 }; CRYPTO_SETTINGS crypto_settings[1]; + PCCERT_CONTEXT client_certs[1]; + + if(client_cert) + client_certs[0] = client_cert; + else + client_certs[0] = NULL; memset(crypto_settings, 0, sizeof(crypto_settings)); @@ -760,8 +620,7 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, credentials.dwVersion = SCH_CREDENTIALS_VERSION; credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO; - credentials.pTlsParameters->grbitDisabledProtocols = - (DWORD)~enabled_protocols; + credentials.pTlsParameters->grbitDisabledProtocols = ~enabled_protocols; if(client_certs[0]) { credentials.cCreds = 1; @@ -781,6 +640,13 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, ALG_ID algIds[NUM_CIPHERS]; char *ciphers = conn_config->cipher_list; SCHANNEL_CRED schannel_cred = { 0 }; + PCCERT_CONTEXT client_certs[1]; + + if(client_cert) + client_certs[0] = client_cert; + else + client_certs[0] = NULL; + schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; schannel_cred.dwFlags = flags; schannel_cred.grbitEnabledProtocols = enabled_protocols; @@ -794,8 +660,6 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); if(result) { failf(data, "schannel: Failed setting algorithm cipher list"); - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); return result; } } @@ -816,9 +680,6 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, &backend->cred->cred_handle, NULL); } - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); - if(sspi_status != SEC_E_OK) { char buffer[STRERROR_LEN]; failf(data, "schannel: AcquireCredentialsHandle failed: %s", @@ -839,6 +700,128 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, return CURLE_OK; } +static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + + PCCERT_CONTEXT client_cert = NULL; + HCERTSTORE client_cert_store = NULL; + CURLcode result; + + /* setup Schannel API options */ + DWORD flags = 0; + DWORD enabled_protocols = 0; + + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)(connssl->backend); + + DEBUGASSERT(backend); + + if(conn_config->verifypeer) { + if(backend->use_manual_cred_validation) + flags = SCH_CRED_MANUAL_CRED_VALIDATION; + else + flags = SCH_CRED_AUTO_CRED_VALIDATION; + + if(ssl_config->no_revoke) { + flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE; + + DEBUGF(infof(data, "schannel: disabled server certificate revocation " + "checks")); + } + else if(ssl_config->revoke_best_effort) { + flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE | + SCH_CRED_REVOCATION_CHECK_CHAIN; + + DEBUGF(infof(data, "schannel: ignore revocation offline errors")); + } + else { + flags |= SCH_CRED_REVOCATION_CHECK_CHAIN; + + DEBUGF(infof(data, "schannel: checking server certificate revocation")); + } + } + else { + flags = SCH_CRED_MANUAL_CRED_VALIDATION | + SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE; + DEBUGF(infof(data, "schannel: disabled server cert revocation checks")); + } + + if(!conn_config->verifyhost) { + flags |= SCH_CRED_NO_SERVERNAME_CHECK; + DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from " + "comparing the supplied target name with the subject " + "names in server certificates.")); + } + + if(!ssl_config->auto_client_cert) { + flags &= ~(DWORD)SCH_CRED_USE_DEFAULT_CREDS; + flags |= SCH_CRED_NO_DEFAULT_CREDS; + infof(data, "schannel: disabled automatic use of client certificate"); + } + else + infof(data, "schannel: enabled automatic use of client certificate"); + + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); + switch(conn_config->version) { + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_3: { + result = schannel_set_ssl_version_min_max(&enabled_protocols, cf, data); + if(result) + return result; + break; + } + case CURL_SSLVERSION_SSLv3: + case CURL_SSLVERSION_SSLv2: + failf(data, "SSL versions not supported"); + return CURLE_NOT_BUILT_IN; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + + result = get_client_cert(data, &client_cert_store, &client_cert); + if(result) + return result; + + /* allocate memory for the reusable credential handle */ + backend->cred = (struct Curl_schannel_cred *) + curlx_calloc(1, sizeof(struct Curl_schannel_cred)); + if(!backend->cred) { + failf(data, "schannel: unable to allocate memory"); + + if(client_cert) + CertFreeCertificateContext(client_cert); + if(client_cert_store) + CertCloseStore(client_cert_store, 0); + + return CURLE_OUT_OF_MEMORY; + } + backend->cred->refcount = 1; + + /* Since we did not persist the key, we need to extend the store's + * lifetime until the end of the connection + */ + backend->cred->client_cert_store = client_cert_store; + + result = acquire_sspi_handle(cf, data, backend, client_cert, + flags, enabled_protocols); + + if(client_cert) + CertFreeCertificateContext(client_cert); + + return result; +} + static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -862,14 +845,6 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", connssl->peer.hostname, connssl->peer.port)); - if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, - VERSION_LESS_THAN_EQUAL)) { - /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and - algorithms that may not be supported by all servers. */ - infof(data, "schannel: Windows version is old and may not be able to " - "connect to some servers due to lack of SNI, algorithms, etc."); - } - #ifdef HAS_ALPN_SCHANNEL backend->use_alpn = connssl->alpn && s_win_has_alpn; #else @@ -989,13 +964,11 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, InitSecBufferDesc(&outbuf_desc, &outbuf, 1); /* security request flags */ - backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_STREAM; - - if(!ssl_config->auto_client_cert) { - backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; - } + backend->req_flags = + ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM | + (!ssl_config->auto_client_cert ? ISC_REQ_USE_SUPPLIED_CREDS : 0); /* allocate memory for the security context handle */ backend->ctxt = (struct Curl_schannel_ctxt *) @@ -1115,6 +1088,132 @@ static CURLcode schannel_error(struct Curl_easy *data, } } +static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *pinnedpubkey) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + CERT_CONTEXT *pCertContextServer = NULL; + + /* Result is returned to caller */ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + DEBUGASSERT(backend); + + /* if a path was not specified, do not pin */ + if(!pinnedpubkey) + return CURLE_OK; + + do { + SECURITY_STATUS sspi_status; + const char *x509_der; + DWORD x509_der_len; + struct Curl_X509certificate x509_parsed; + struct Curl_asn1Element *pubkey; + + sspi_status = + Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); + + if((sspi_status != SEC_E_OK) || !pCertContextServer) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: Failed to read remote certificate context: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + break; /* failed */ + } + + if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && + (pCertContextServer->cbCertEncoded > 0))) + break; + + x509_der = (const char *)pCertContextServer->pbCertEncoded; + x509_der_len = pCertContextServer->cbCertEncoded; + memset(&x509_parsed, 0, sizeof(x509_parsed)); + if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) + break; + + pubkey = &x509_parsed.subjectPublicKeyInfo; + if(!pubkey->header || pubkey->end <= pubkey->header) { + failf(data, "SSL: failed retrieving public key from server certificate"); + break; + } + + result = Curl_pin_peer_pubkey(data, + pinnedpubkey, + (const unsigned char *)pubkey->header, + (size_t)(pubkey->end - pubkey->header)); + if(result) { + failf(data, "SSL: public key does not match pinned public key"); + } + } while(0); + + if(pCertContextServer) + CertFreeCertificateContext(pCertContextServer); + + return result; +} + +static CURLcode ensure_encoding_size(struct Curl_easy *data, + struct sbuffer *encdata, + size_t min_length) +{ + size_t size; + DEBUGASSERT(encdata->length >= encdata->offset); + if(encdata->length < encdata->offset) + return CURLE_FAILED_INIT; + size = encdata->length - encdata->offset; + if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || encdata->length < min_length) { + unsigned char *buffer; + size_t length = encdata->offset + CURL_SCHANNEL_BUFFER_FREE_SIZE; + if(length < min_length) + length = min_length; + buffer = curlx_realloc(encdata->buffer, length); + if(!buffer) { + failf(data, "schannel: unable to re-allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + + encdata->buffer = buffer; + encdata->length = length; + SCH_DEV(infof(data, "schannel: encdata.buffer resized %zu", + encdata->length)); + } + return CURLE_OK; +} + +static CURLcode ensure_decoding_size(struct Curl_easy *data, + struct sbuffer *decdata, + size_t nowsize, + size_t len) +{ + size_t size = nowsize > CURL_SCHANNEL_BUFFER_FREE_SIZE ? + nowsize : CURL_SCHANNEL_BUFFER_FREE_SIZE; + DEBUGASSERT(decdata->length >= decdata->offset); + if(decdata->length < decdata->offset) + return CURLE_FAILED_INIT; + else if(decdata->length - decdata->offset < size || + decdata->length < len) { + /* increase internal decrypted data buffer */ + size_t length = decdata->offset + size; + unsigned char *buffer; + /* make sure that the requested amount of data fits */ + if(length < len) + length = len; + + buffer = curlx_realloc(decdata->buffer, length); + if(!buffer) { + failf(data, "schannel: unable to re-allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + decdata->buffer = buffer; + decdata->length = length; + } + return CURLE_OK; +} + static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1123,8 +1222,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, (struct schannel_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); int i; - size_t nread = 0, written = 0; - unsigned char *reallocated_buffer; + size_t nread = 0; SecBuffer outbuf[3]; SecBufferDesc outbuf_desc; SecBuffer inbuf[2]; @@ -1146,58 +1244,43 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; /* buffer to store previously received and decrypted data */ - if(!backend->decdata_buffer) { - backend->decdata_offset = 0; - backend->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - backend->decdata_buffer = curlx_malloc(backend->decdata_length); - if(!backend->decdata_buffer) { + if(!backend->decdata.buffer) { + backend->decdata.offset = 0; + backend->decdata.length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + backend->decdata.buffer = curlx_malloc(backend->decdata.length); + if(!backend->decdata.buffer) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } } /* buffer to store previously received and encrypted data */ - if(!backend->encdata_buffer) { + if(!backend->encdata.buffer) { backend->encdata_is_incomplete = FALSE; - backend->encdata_offset = 0; - backend->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - backend->encdata_buffer = curlx_malloc(backend->encdata_length); - if(!backend->encdata_buffer) { + backend->encdata.offset = 0; + backend->encdata.length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + backend->encdata.buffer = curlx_malloc(backend->encdata.length); + if(!backend->encdata.buffer) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } } - /* if we need a bigger buffer to read a full message, increase buffer now */ - if(backend->encdata_length - backend->encdata_offset < - CURL_SCHANNEL_BUFFER_FREE_SIZE) { - /* increase internal encrypted data buffer */ - size_t reallocated_length = backend->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - reallocated_buffer = curlx_realloc(backend->encdata_buffer, - reallocated_length); - - if(!reallocated_buffer) { - failf(data, "schannel: unable to re-allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - else { - backend->encdata_buffer = reallocated_buffer; - backend->encdata_length = reallocated_length; - } - } + result = ensure_encoding_size(data, &backend->encdata, 0); + if(result) + return result; for(;;) { if(doread) { /* read encrypted handshake data from socket */ result = Curl_conn_cf_recv(cf->next, data, - (char *)(backend->encdata_buffer + - backend->encdata_offset), - backend->encdata_length - - backend->encdata_offset, + (char *)(backend->encdata.buffer + + backend->encdata.offset), + backend->encdata.length - + backend->encdata.offset, &nread); if(result == CURLE_AGAIN) { - if(!backend->encdata_offset || backend->encdata_is_incomplete) { + if(!backend->encdata.offset || backend->encdata_is_incomplete) { connssl->io_need = CURL_SSL_IO_NEED_RECV; DEBUGF(infof(data, "schannel: failed to receive handshake, " "need more data")); @@ -1215,7 +1298,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, } else { /* increase encrypted data buffer offset */ - backend->encdata_offset += nread; + backend->encdata.offset += nread; backend->encdata_is_incomplete = FALSE; SCH_DEV(infof(data, "schannel: encrypted data got %zu", nread)); } @@ -1223,12 +1306,12 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); /* setup input buffers */ InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, - curlx_malloc(backend->encdata_offset), - curlx_uztoul(backend->encdata_offset)); + curlx_malloc(backend->encdata.offset), + curlx_uztoul(backend->encdata.offset)); InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&inbuf_desc, inbuf, 2); @@ -1244,8 +1327,8 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, } /* copy received handshake data into input buffer */ - memcpy(inbuf[0].pvBuffer, backend->encdata_buffer, - backend->encdata_offset); + memcpy(inbuf[0].pvBuffer, backend->encdata.buffer, + backend->encdata.offset); /* The socket must be writeable (or a poll error occurred) before we call InitializeSecurityContext to continue processing the received TLS @@ -1283,6 +1366,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, for(i = 0; i < 3; i++) { /* search for handshake tokens that need to be send */ if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { + size_t written = 0; DEBUGF(infof(data, "schannel: sending next handshake data: " "sending %lu bytes.", outbuf[i].cbBuffer)); @@ -1331,22 +1415,21 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, inbuf[1].cbBuffer)); /* There are two cases where we could be getting extra data here: - 1) If we are renegotiating a connection and the handshake is already - complete (from the server perspective), it can encrypted app data - (not handshake data) in an extra buffer at this point. - 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a - connection and this extra data is part of the handshake. - We should process the data immediately; waiting for the socket to - be ready may fail since the server is done sending handshake data. + 1. If we are renegotiating a connection and the handshake is already + complete (from the server perspective), it can encrypted app data + (not handshake data) in an extra buffer at this point. + 2. (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a + connection and this extra data is part of the handshake. + We should process the data immediately; waiting for the socket to + be ready may fail since the server is done sending handshake data. */ /* check if the remaining data is less than the total amount and therefore begins after the already processed data */ - if(backend->encdata_offset > inbuf[1].cbBuffer) { - memmove(backend->encdata_buffer, - (backend->encdata_buffer + backend->encdata_offset) - - inbuf[1].cbBuffer, - inbuf[1].cbBuffer); - backend->encdata_offset = inbuf[1].cbBuffer; + if(backend->encdata.offset > inbuf[1].cbBuffer) { + memmove(backend->encdata.buffer, + (backend->encdata.buffer + backend->encdata.offset) - + inbuf[1].cbBuffer, inbuf[1].cbBuffer); + backend->encdata.offset = inbuf[1].cbBuffer; if(sspi_status == SEC_I_CONTINUE_NEEDED) { doread = FALSE; continue; @@ -1354,7 +1437,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, } } else { - backend->encdata_offset = 0; + backend->encdata.offset = 0; } break; } @@ -1531,8 +1614,8 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, if(backend->use_alpn) { sspi_status = Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_APPLICATION_PROTOCOL, - &alpn_result); + SECPKG_ATTR_APPLICATION_PROTOCOL, + &alpn_result); if(sspi_status != SEC_E_OK) { failf(data, "schannel: failed to retrieve ALPN result"); @@ -1577,8 +1660,8 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, int certs_count = 0; sspi_status = Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &ccert_context); + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &ccert_context); if((sspi_status != SEC_E_OK) || !ccert_context) { failf(data, "schannel: failed to retrieve remote cert context"); @@ -1681,9 +1764,9 @@ enum schannel_renegotiate_caller_t { incomplete. In that case, we remain in the renegotiation (connecting) stage and future calls to schannel_recv and schannel_send must call this function first to complete the renegotiation. */ -static CURLcode -schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, - enum schannel_renegotiate_caller_t caller) +static CURLcode schannel_recv_renegotiate( + struct Curl_cfilter *cf, struct Curl_easy *data, + enum schannel_renegotiate_caller_t caller) { CURLcode result; curl_socket_t sockfd; @@ -1801,7 +1884,7 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, remaining = MAX_RENEG_BLOCK_TIME - elapsed; if(blocking) { - timeout_ms = Curl_timeleft_ms(data, FALSE); + timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; @@ -1954,11 +2037,11 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, while(len > *pnwritten) { size_t this_write = 0; int what; - timediff_t timeout_ms = Curl_timeleft_ms(data, FALSE); + timediff_t timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { /* we already got the timeout */ - failf(data, "schannel: timed out sending data " - "(bytes sent: %zu)", *pnwritten); + failf(data, "schannel: timed out sending data (bytes sent: %zu)", + *pnwritten); result = CURLE_OPERATION_TIMEDOUT; break; } @@ -1972,8 +2055,8 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, break; } else if(what == 0) { - failf(data, "schannel: timed out sending data " - "(bytes sent: %zu)", *pnwritten); + failf(data, "schannel: timed out sending data (bytes sent: %zu)", + *pnwritten); result = CURLE_OPERATION_TIMEDOUT; break; } @@ -2015,14 +2098,9 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, size_t size = 0; size_t nread = 0; struct ssl_connect_data *connssl = cf->ctx; - unsigned char *reallocated_buffer; - size_t reallocated_length; SecBuffer inbuf[4]; SecBufferDesc inbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; - /* we want the length of the encrypted buffer to be at least large enough - that it can hold all the bytes requested and some TLS record overhead. */ - size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; @@ -2054,7 +2132,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, SCH_DEV(infof(data, "schannel: client wants to read %zu bytes", len)); - if(len && len <= backend->decdata_offset) { + if(len && len <= backend->decdata.offset) { SCH_DEV(infof(data, "schannel: enough decrypted data is already available")); goto cleanup; @@ -2074,68 +2152,56 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, decrypt all encrypted cached data) so handle !len later in cleanup. */ else if(len && !backend->recv_connection_closed) { - /* increase enc buffer in order to fit the requested amount of data */ - size = backend->encdata_length - backend->encdata_offset; - if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || - backend->encdata_length < min_encdata_length) { - reallocated_length = backend->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(reallocated_length < min_encdata_length) { - reallocated_length = min_encdata_length; - } - reallocated_buffer = curlx_realloc(backend->encdata_buffer, - reallocated_length); - if(!reallocated_buffer) { - result = CURLE_OUT_OF_MEMORY; - failf(data, "schannel: unable to re-allocate memory"); - goto cleanup; - } + /* the encrypted buffer must be large enough to hold all the bytes + requested and some TLS record overhead. 'len' is a buffer size, so this + integer math cannot overflow. */ + const size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; - backend->encdata_buffer = reallocated_buffer; - backend->encdata_length = reallocated_length; - size = backend->encdata_length - backend->encdata_offset; - SCH_DEV(infof(data, "schannel: encdata_buffer resized %zu", - backend->encdata_length)); - } + /* make sure encrypt buffer fits the requested amount of data */ + result = ensure_encoding_size(data, &backend->encdata, min_encdata_length); + if(result) + goto cleanup; SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); /* read encrypted data from socket */ result = Curl_conn_cf_recv(cf->next, data, - (char *)(backend->encdata_buffer + - backend->encdata_offset), - size, &nread); + (char *)(backend->encdata.buffer + + backend->encdata.offset), + backend->encdata.length - + backend->encdata.offset, + &nread); if(result) { if(result == CURLE_AGAIN) SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN")); - else if(result == CURLE_RECV_ERROR) - infof(data, "schannel: recv returned CURLE_RECV_ERROR"); - else + else { infof(data, "schannel: recv returned error %d", result); + backend->recv_unrecoverable_err = result; + } } else if(nread == 0) { backend->recv_connection_closed = TRUE; DEBUGF(infof(data, "schannel: server closed the connection")); } else { - backend->encdata_offset += nread; + backend->encdata.offset += nread; backend->encdata_is_incomplete = FALSE; SCH_DEV(infof(data, "schannel: encrypted data got %zu", nread)); } } SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); /* decrypt loop */ - while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK && - (!len || backend->decdata_offset < len || + while(backend->encdata.offset > 0 && sspi_status == SEC_E_OK && + (!len || backend->decdata.offset < len || backend->recv_connection_closed)) { /* prepare data buffer for DecryptMessage call */ - InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata_buffer, - curlx_uztoul(backend->encdata_offset)); + InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata.buffer, + curlx_uztoul(backend->encdata.offset)); /* we need 3 more empty input buffers for possible output */ InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); @@ -2158,40 +2224,24 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, SCH_DEV(infof(data, "schannel: decrypted data length: %lu", inbuf[1].cbBuffer)); - /* increase buffer in order to fit the received amount of data */ - size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? - inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(backend->decdata_length - backend->decdata_offset < size || - backend->decdata_length < len) { - /* increase internal decrypted data buffer */ - reallocated_length = backend->decdata_offset + size; - /* make sure that the requested amount of data fits */ - if(reallocated_length < len) { - reallocated_length = len; - } - reallocated_buffer = curlx_realloc(backend->decdata_buffer, - reallocated_length); - if(!reallocated_buffer) { - result = CURLE_OUT_OF_MEMORY; - failf(data, "schannel: unable to re-allocate memory"); - goto cleanup; - } - backend->decdata_buffer = reallocated_buffer; - backend->decdata_length = reallocated_length; - } + /* ensure the decode buffer fits the received amount of data */ + result = ensure_decoding_size(data, &backend->decdata, + inbuf[1].cbBuffer, len); + if(result) + goto cleanup; /* copy decrypted data to internal buffer */ size = inbuf[1].cbBuffer; if(size) { - memcpy(backend->decdata_buffer + backend->decdata_offset, + memcpy(backend->decdata.buffer + backend->decdata.offset, inbuf[1].pvBuffer, size); - backend->decdata_offset += size; + backend->decdata.offset += size; } SCH_DEV(infof(data, "schannel: decrypted data added: %zu", size)); SCH_DEV(infof(data, "schannel: decrypted cached: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + backend->decdata.offset, backend->decdata.length)); } /* check for remaining encrypted data */ @@ -2202,23 +2252,22 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* check if the remaining data is less than the total amount * and therefore begins after the already processed data */ - if(backend->encdata_offset > inbuf[3].cbBuffer) { + if(backend->encdata.offset > inbuf[3].cbBuffer) { /* move remaining encrypted data forward to the beginning of buffer */ - memmove(backend->encdata_buffer, - (backend->encdata_buffer + backend->encdata_offset) - - inbuf[3].cbBuffer, - inbuf[3].cbBuffer); - backend->encdata_offset = inbuf[3].cbBuffer; + memmove(backend->encdata.buffer, + (backend->encdata.buffer + backend->encdata.offset) - + inbuf[3].cbBuffer, inbuf[3].cbBuffer); + backend->encdata.offset = inbuf[3].cbBuffer; } SCH_DEV(infof(data, "schannel: encrypted cached: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); } else { /* reset encrypted buffer offset, because there is no data remaining */ - backend->encdata_offset = 0; + backend->encdata.offset = 0; } /* check if server wants to renegotiate the connection context */ @@ -2245,7 +2294,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->recv_sspi_close_notify = TRUE; if(!backend->recv_connection_closed) backend->recv_connection_closed = TRUE; - /* We received the close notify just fine, any error we got + /* We received the close notify fine, any error we got * from the lower filters afterwards (e.g. the socket), is not * an error on the TLS data stream. That one ended here. */ if(result == CURLE_RECV_ERROR) @@ -2263,21 +2312,19 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto cleanup; } else { -#ifndef CURL_DISABLE_VERBOSE_STRINGS char buffer[STRERROR_LEN]; failf(data, "schannel: failed to read data from server: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); -#endif result = CURLE_RECV_ERROR; goto cleanup; } } SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + backend->encdata.offset, backend->encdata.length)); SCH_DEV(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + backend->decdata.offset, backend->decdata.length)); cleanup: /* Warning- there is no guarantee the encdata state is valid at this point */ @@ -2289,7 +2336,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, to a truncation attack however there is some browser precedent for ignoring the close_notify for compatibility reasons. */ - if(len && !backend->decdata_offset && backend->recv_connection_closed && + if(len && !backend->decdata.offset && backend->recv_connection_closed && !backend->recv_sspi_close_notify) { result = CURLE_RECV_ERROR; failf(data, "schannel: server closed abruptly (missing close_notify)"); @@ -2299,16 +2346,16 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(result && result != CURLE_AGAIN) backend->recv_unrecoverable_err = result; - size = len < backend->decdata_offset ? len : backend->decdata_offset; + size = len < backend->decdata.offset ? len : backend->decdata.offset; if(size) { - memcpy(buf, backend->decdata_buffer, size); - memmove(backend->decdata_buffer, backend->decdata_buffer + size, - backend->decdata_offset - size); - backend->decdata_offset -= size; + memcpy(buf, backend->decdata.buffer, size); + memmove(backend->decdata.buffer, backend->decdata.buffer + size, + backend->decdata.offset - size); + backend->decdata.offset -= size; SCH_DEV(infof(data, "schannel: decrypted data returned %zu", size)); SCH_DEV(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + backend->decdata.offset, backend->decdata.length)); *pnread = size; return CURLE_OK; } @@ -2337,8 +2384,8 @@ static bool schannel_data_pending(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(backend->ctxt) /* SSL/TLS is in use */ - return backend->decdata_offset > 0 || - (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) || + return backend->decdata.offset > 0 || + (backend->encdata.offset > 0 && !backend->encdata_is_incomplete) || backend->recv_connection_closed || backend->recv_sspi_close_notify || backend->recv_unrecoverable_err; @@ -2395,7 +2442,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, InitSecBufferDesc(&BuffDesc, &Buffer, 1); sspi_status = Curl_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle, - &BuffDesc); + &BuffDesc); if(sspi_status != SEC_E_OK) { char buffer[STRERROR_LEN]; @@ -2515,18 +2562,18 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) } /* free internal buffer for received encrypted data */ - if(backend->encdata_buffer) { - Curl_safefree(backend->encdata_buffer); - backend->encdata_length = 0; - backend->encdata_offset = 0; + if(backend->encdata.buffer) { + Curl_safefree(backend->encdata.buffer); + backend->encdata.length = 0; + backend->encdata.offset = 0; backend->encdata_is_incomplete = FALSE; } /* free internal buffer for received decrypted data */ - if(backend->decdata_buffer) { - Curl_safefree(backend->decdata_buffer); - backend->decdata_length = 0; - backend->decdata_offset = 0; + if(backend->decdata.buffer) { + Curl_safefree(backend->decdata.buffer); + backend->decdata.length = 0; + backend->decdata.offset = 0; } } @@ -2580,74 +2627,6 @@ static CURLcode schannel_random(struct Curl_easy *data, return Curl_win32_random(entropy, length); } -static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *pinnedpubkey) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct schannel_ssl_backend_data *backend = - (struct schannel_ssl_backend_data *)connssl->backend; - CERT_CONTEXT *pCertContextServer = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - DEBUGASSERT(backend); - - /* if a path was not specified, do not pin */ - if(!pinnedpubkey) - return CURLE_OK; - - do { - SECURITY_STATUS sspi_status; - const char *x509_der; - DWORD x509_der_len; - struct Curl_X509certificate x509_parsed; - struct Curl_asn1Element *pubkey; - - sspi_status = - Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &pCertContextServer); - - if((sspi_status != SEC_E_OK) || !pCertContextServer) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: Failed to read remote certificate context: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - break; /* failed */ - } - - if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (pCertContextServer->cbCertEncoded > 0))) - break; - - x509_der = (const char *)pCertContextServer->pbCertEncoded; - x509_der_len = pCertContextServer->cbCertEncoded; - memset(&x509_parsed, 0, sizeof(x509_parsed)); - if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) - break; - - pubkey = &x509_parsed.subjectPublicKeyInfo; - if(!pubkey->header || pubkey->end <= pubkey->header) { - failf(data, "SSL: failed retrieving public key from server certificate"); - break; - } - - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, - (const unsigned char *)pubkey->header, - (size_t)(pubkey->end - pubkey->header)); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - } - } while(0); - - if(pCertContextServer) - CertFreeCertificateContext(pCertContextServer); - - return result; -} - static void schannel_checksum(const unsigned char *input, size_t inputlen, unsigned char *checksum, @@ -2657,9 +2636,6 @@ static void schannel_checksum(const unsigned char *input, { HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; - DWORD cbHashSize = 0; - DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); - DWORD dwChecksumLen = (DWORD)checksumlen; /* since this can fail in multiple ways, zero memory first so we never * return old data @@ -2671,6 +2647,10 @@ static void schannel_checksum(const unsigned char *input, return; /* failed */ do { + DWORD cbHashSize = 0; + DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); + DWORD dwChecksumLen = (DWORD)checksumlen; + if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) break; /* failed */ @@ -2726,7 +2706,6 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, struct schannel_cert_share *share; const struct ssl_general_config *cfg = &data->set.general_ssl; timediff_t timeout_ms; - timediff_t elapsed_ms; unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH]; DEBUGASSERT(multi); @@ -2752,7 +2731,8 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, negative timeout means retain forever. */ timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; if(timeout_ms >= 0) { - elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &share->time); + timediff_t elapsed_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &share->time); if(elapsed_ms >= timeout_ms) { return NULL; } diff --git a/vendor/curl/lib/vtls/schannel.h b/vendor/curl/lib/vtls/schannel.h index f66bcf2..fe49e9a 100644 --- a/vendor/curl/lib/vtls/schannel.h +++ b/vendor/curl/lib/vtls/schannel.h @@ -24,55 +24,15 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4201) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif -/* Wincrypt must be included before anything that could include OpenSSL. */ -#ifdef USE_WIN32_CRYPTO -#include -/* Undefine wincrypt conflicting symbols for BoringSSL. */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#endif - -#include #include -#include "../curl_sspi.h" - -#include "../cfilters.h" -#include "../urldata.h" -/* has been included via the above . - * Or in case of ldap.c, it was included via . - * And since has this: - * #define X509_NAME ((LPCSTR)7) - * - * And in BoringSSL's there is: - * typedef struct X509_name_st X509_NAME; - * etc. - * - * this will cause all kinds of C-preprocessing paste errors in - * BoringSSL's : So just undefine those defines here - * (and only here). - */ -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) -#undef X509_NAME -#undef X509_CERT_PAIR -#undef X509_EXTENSIONS -#endif +#include "curl_sspi.h" +#include "cfilters.h" +#include "urldata.h" extern const struct Curl_ssl Curl_ssl_schannel; diff --git a/vendor/curl/lib/vtls/schannel_int.h b/vendor/curl/lib/vtls/schannel_int.h index 0146bf8..496635f 100644 --- a/vendor/curl/lib/vtls/schannel_int.h +++ b/vendor/curl/lib/vtls/schannel_int.h @@ -24,12 +24,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL -#include "vtls.h" -#include "../curl_sha256.h" +#include "vtls/vtls.h" +#include "curl_sha256.h" #if defined(_MSC_VER) && (_MSC_VER < 1700) /* Workaround for warning: @@ -91,11 +91,6 @@ typedef struct _SCH_CREDENTIALS { PTLS_PARAMETERS pTlsParameters; } SCH_CREDENTIALS, * PSCH_CREDENTIALS; -#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 -#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16 -#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16 -#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16 - #endif /* SCH_CREDENTIALS_VERSION */ struct Curl_schannel_cred { @@ -109,17 +104,19 @@ struct Curl_schannel_ctxt { CtxtHandle ctxt_handle; }; +/* handle encoding/decoding buffers */ +struct sbuffer { + size_t length; + size_t offset; + unsigned char *buffer; +}; + struct schannel_ssl_backend_data { + struct sbuffer encdata; + struct sbuffer decdata; struct Curl_schannel_cred *cred; struct Curl_schannel_ctxt *ctxt; SecPkgContext_StreamSizes stream_sizes; - size_t encdata_length, decdata_length; - size_t encdata_offset, decdata_offset; - unsigned char *encdata_buffer, *decdata_buffer; - /* encdata_is_incomplete: if encdata contains only a partial record that - cannot be decrypted without another recv() (that is, status is - SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds - more bytes into encdata then set this back to false. */ unsigned long req_flags, ret_flags; CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ struct schannel_renegotiate_state { @@ -133,6 +130,10 @@ struct schannel_ssl_backend_data { BIT(use_alpn); /* true if ALPN is used for this connection */ BIT(use_manual_cred_validation); /* true if manual cred validation is used */ BIT(sent_shutdown); + /* encdata_is_incomplete: if encdata contains only a partial record that + cannot be decrypted without another recv() (that is, status is + SEC_E_INCOMPLETE_MESSAGE) then set this true. after recv() adds more + bytes into encdata then set this back to false. */ BIT(encdata_is_incomplete); }; diff --git a/vendor/curl/lib/vtls/schannel_verify.c b/vendor/curl/lib/vtls/schannel_verify.c index 5129b8b..414b2e9 100644 --- a/vendor/curl/lib/vtls/schannel_verify.c +++ b/vendor/curl/lib/vtls/schannel_verify.c @@ -27,30 +27,31 @@ * Source file for Schannel-specific certificate verification. This code should * only be invoked by code in schannel.c. */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL + #ifndef USE_WINDOWS_SSPI -#error "cannot compile SCHANNEL support without SSPI." +#error "cannot compile Schannel support without SSPI." #endif -#include "schannel.h" -#include "schannel_int.h" +#include "vtls/schannel.h" +#include "vtls/schannel_int.h" -#include "../curlx/fopen.h" -#include "../curlx/inet_pton.h" -#include "vtls.h" -#include "vtls_int.h" -#include "../curl_trc.h" -#include "../strerror.h" -#include "../curlx/winapi.h" -#include "../curlx/multibyte.h" -#include "hostcheck.h" -#include "../curlx/version_win32.h" +#include "vtls/hostcheck.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "curl_trc.h" +#include "strerror.h" +#include "curlx/fopen.h" +#include "curlx/inet_pton.h" +#include "curlx/multibyte.h" +#include "curlx/version_win32.h" +#include "curlx/winapi.h" #define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend) -#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ +#define MAX_CAFILE_SIZE (1024 * 1024) /* 1 MiB */ #define BEGIN_CERT "-----BEGIN CERTIFICATE-----" #define END_CERT "\n-----END CERTIFICATE-----" @@ -437,10 +438,8 @@ static DWORD cert_get_name_string(struct Curl_easy *data, static bool get_num_host_info(struct num_ip_data *ip_blob, LPCSTR hostname) { - struct in_addr ia; - struct in6_addr ia6; bool result = FALSE; - + struct in_addr ia; int res = curlx_inet_pton(AF_INET, hostname, &ia); if(res) { ip_blob->size = sizeof(struct in_addr); @@ -448,6 +447,7 @@ static bool get_num_host_info(struct num_ip_data *ip_blob, LPCSTR hostname) result = TRUE; } else { + struct in6_addr ia6; res = curlx_inet_pton(AF_INET6, hostname, &ia6); if(res) { ip_blob->size = sizeof(struct in6_addr); diff --git a/vendor/curl/lib/vtls/vtls.c b/vendor/curl/lib/vtls/vtls.c index be0c032..f7201d1 100644 --- a/vendor/curl/lib/vtls/vtls.c +++ b/vendor/curl/lib/vtls/vtls.c @@ -38,40 +38,40 @@ https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_SYS_TYPES_H #include #endif -#include "../urldata.h" -#include "../cfilters.h" - -#include "vtls.h" /* generic SSL protos etc */ -#include "vtls_int.h" -#include "vtls_scache.h" - -#include "openssl.h" /* OpenSSL versions */ -#include "gtls.h" /* GnuTLS versions */ -#include "wolfssl.h" /* wolfSSL versions */ -#include "schannel.h" /* Schannel SSPI version */ -#include "mbedtls.h" /* mbedTLS versions */ -#include "rustls.h" /* Rustls versions */ - -#include "../slist.h" -#include "../curl_trc.h" -#include "../strcase.h" -#include "../url.h" -#include "../progress.h" -#include "../curlx/fopen.h" -#include "../curl_sha256.h" -#include "../curlx/base64.h" -#include "../curlx/inet_pton.h" -#include "../connect.h" -#include "../select.h" -#include "../setopt.h" -#include "../strdup.h" -#include "../curlx/strcopy.h" +#include "urldata.h" +#include "cfilters.h" + +#include "vtls/vtls.h" /* generic SSL protos etc */ +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" + +#include "vtls/openssl.h" /* OpenSSL versions */ +#include "vtls/gtls.h" /* GnuTLS versions */ +#include "vtls/wolfssl.h" /* wolfSSL versions */ +#include "vtls/schannel.h" /* Schannel SSPI version */ +#include "vtls/mbedtls.h" /* mbedTLS versions */ +#include "vtls/rustls.h" /* Rustls versions */ + +#include "slist.h" +#include "curl_trc.h" +#include "strcase.h" +#include "url.h" +#include "progress.h" +#include "curlx/fopen.h" +#include "curl_sha256.h" +#include "curlx/base64.h" +#include "curlx/inet_pton.h" +#include "connect.h" +#include "select.h" +#include "setopt.h" +#include "curlx/strdup.h" +#include "curlx/strcopy.h" #ifdef USE_APPLE_SECTRUST #include @@ -133,7 +133,9 @@ static bool blobcmp(struct curl_blob *first, struct curl_blob *second) static const struct alpn_spec ALPN_SPEC_H11 = { { ALPN_HTTP_1_1 }, 1 }; -#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ +static const struct alpn_spec ALPN_SPEC_H10_H11 = { + { ALPN_HTTP_1_0, ALPN_HTTP_1_1 }, 2 +}; #ifdef USE_HTTP2 static const struct alpn_spec ALPN_SPEC_H2 = { { ALPN_H2 }, 1 @@ -144,15 +146,20 @@ static const struct alpn_spec ALPN_SPEC_H2_H11 = { static const struct alpn_spec ALPN_SPEC_H11_H2 = { { ALPN_HTTP_1_1, ALPN_H2 }, 2 }; -#endif +#endif /* USE_HTTP2 */ -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_PROXY) static const struct alpn_spec *alpn_get_spec(http_majors wanted, http_majors preferred, + bool only_http_10, bool use_alpn) { if(!use_alpn) return NULL; + /* If HTTP/1.0 is the wanted protocol then use ALPN http/1.0 and http/1.1. + This is for compatibility reasons since some HTTP/1.0 servers with old + ALPN implementations understand ALPN http/1.1 but not http/1.0. */ + if(only_http_10 && (wanted & CURL_HTTP_V1x)) + return &ALPN_SPEC_H10_H11; #ifdef USE_HTTP2 if(wanted & CURL_HTTP_V2x) { if(wanted & CURL_HTTP_V1x) @@ -164,8 +171,6 @@ static const struct alpn_spec *alpn_get_spec(http_majors wanted, (void)wanted; (void)preferred; #endif - /* Use the ALPN protocol "http/1.1" for HTTP/1.x. - Avoid "http/1.0" because some servers do not support it. */ return &ALPN_SPEC_H11; } #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ @@ -295,7 +300,7 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) #endif if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#ifdef USE_APPLE_SECTRUST +#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) sslc->native_ca_store = TRUE; #endif @@ -341,7 +346,7 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) #ifndef CURL_DISABLE_PROXY sslc = &data->set.proxy_ssl; if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#ifdef USE_APPLE_SECTRUST +#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) sslc->native_ca_store = TRUE; #endif @@ -672,12 +677,11 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, /* get length bytes of randomness */ CURLcode Curl_ssl_random(struct Curl_easy *data, - unsigned char *entropy, - size_t length) + unsigned char *buffer, size_t length) { DEBUGASSERT(length == sizeof(int)); if(Curl_ssl->random) - return Curl_ssl->random(data, entropy, length); + return Curl_ssl->random(data, buffer, length); else return CURLE_NOT_BUILT_IN; } @@ -689,7 +693,7 @@ CURLcode Curl_ssl_random(struct Curl_easy *data, static CURLcode pubkey_pem_to_der(const char *pem, unsigned char **der, size_t *der_len) { - char *begin_pos, *end_pos; + const char *begin_pos, *end_pos; size_t pem_count, pem_len; CURLcode result; struct dynbuf pbuf; @@ -752,9 +756,6 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, const unsigned char *pubkey, size_t pubkeylen) { CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif /* if a path was not specified, do not pin */ if(!pinnedpubkey) @@ -1061,7 +1062,7 @@ static size_t multissl_version(char *buffer, size_t size) if(current != selected) { char *p = backends; - char *end = backends + sizeof(backends); + const char *end = backends + sizeof(backends); int i; selected = current; @@ -1719,7 +1720,8 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, #else ctx = cf_ctx_new(data, alpn_get_spec(data->state.http_neg.wanted, data->state.http_neg.preferred, - conn->bits.tls_enable_alpn)); + (bool)data->state.http_neg.only_10, + (bool)conn->bits.tls_enable_alpn)); #endif if(!ctx) { result = CURLE_OUT_OF_MEMORY; @@ -1770,7 +1772,7 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, struct ssl_connect_data *ctx; CURLcode result; /* ALPN is default, but if user explicitly disables it, obey */ - bool use_alpn = data->set.ssl_enable_alpn; + bool use_alpn = (bool)data->set.ssl_enable_alpn; http_majors wanted = CURL_HTTP_V1x; (void)conn; @@ -1781,7 +1783,7 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, } #endif - ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, use_alpn)); + ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, false, use_alpn)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -2042,7 +2044,7 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, result = CURLE_SSL_CONNECT_ERROR; goto out; } - connssl->negotiated.alpn = Curl_memdup0((const char *)proto, proto_len); + connssl->negotiated.alpn = curlx_memdup0((const char *)proto, proto_len); if(!connssl->negotiated.alpn) return CURLE_OUT_OF_MEMORY; } @@ -2064,4 +2066,34 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, return result; } +CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data, bool early_data_allowed) +{ + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result = CURLE_OK; + + *do_early_data = FALSE; + + if(!early_data_allowed) { + CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); + } + else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { + CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); + } + else { + infof(data, "SSL session allows %zu bytes of early data, " + "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); + connssl->earlydata_state = ssl_earlydata_await; + connssl->state = ssl_connection_deferred; + result = Curl_alpn_set_negotiated(cf, data, connssl, + (const unsigned char *)scs->alpn, + scs->alpn ? strlen(scs->alpn) : 0); + *do_early_data = !result; + } + return result; +} + #endif /* USE_SSL */ diff --git a/vendor/curl/lib/vtls/vtls.h b/vendor/curl/lib/vtls/vtls.h index 112681a..b9335bb 100644 --- a/vendor/curl/lib/vtls/vtls.h +++ b/vendor/curl/lib/vtls/vtls.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" struct connectdata; struct ssl_config_data; @@ -43,11 +43,15 @@ struct dynbuf; #define SSLSUPP_CA_CACHE (1 << 8) #define SSLSUPP_CIPHER_LIST (1 << 9) /* supports TLS 1.0-1.2 ciphersuites */ #define SSLSUPP_SIGNATURE_ALGORITHMS (1 << 10) /* supports TLS sigalgs */ +#define SSLSUPP_ISSUERCERT (1 << 11) /* supports CURLOPT_ISSUERCERT */ +#define SSLSUPP_SSL_EC_CURVES (1 << 12) /* supports CURLOPT_SSL_EC_CURVES */ +#define SSLSUPP_CRLFILE (1 << 13) /* supports CURLOPT_CRLFILE */ +#define SSLSUPP_ISSUERCERT_BLOB (1 << 14) /* CURLOPT_ISSUERCERT_BLOB */ #ifdef USE_ECH -# include "../curlx/base64.h" -# define ECH_ENABLED(__data__) \ - (__data__->set.tls_ech && !(__data__->set.tls_ech & CURLECH_DISABLE)) +#include "curlx/base64.h" +#define ECH_ENABLED(data) \ + ((data)->set.tls_ech && !((data)->set.tls_ech & CURLECH_DISABLE)) #endif /* USE_ECH */ #define ALPN_ACCEPTED "ALPN: server accepted " @@ -93,9 +97,7 @@ struct ssl_peer { CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, const curl_ssl_backend ***avail); -#ifndef MAX_PINNED_PUBKEY_SIZE -#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1 MiB */ -#endif +#define MAX_PINNED_PUBKEY_SIZE (1024 * 1024) /* 1 MiB */ curl_sslbackend Curl_ssl_backend(void); @@ -176,8 +178,8 @@ CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum, /* Functions to be used by SSL library adaptation functions */ /* get N random bytes into the buffer */ -CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer, - size_t length); +CURLcode Curl_ssl_random(struct Curl_easy *data, + unsigned char *buffer, size_t length); /* Check pinned public key. */ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, const char *pinnedpubkey, @@ -247,7 +249,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #else /* if not USE_SSL */ -/* When SSL support is not present, just define away these function calls */ +/* When SSL support is not present, define away these function calls */ #define Curl_ssl_init() 1 #define Curl_ssl_cleanup() Curl_nop_stmt #define Curl_ssl_close_all(x) Curl_nop_stmt @@ -255,7 +257,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN #define Curl_ssl_engines_list(x) NULL #define Curl_ssl_free_certinfo(x) Curl_nop_stmt -#define Curl_ssl_random(x, y, z) ((void)x, CURLE_NOT_BUILT_IN) +#define Curl_ssl_random(x, y, z) ((void)(x), CURLE_NOT_BUILT_IN) #define Curl_ssl_cert_status_request() FALSE #define Curl_ssl_supports(a, b) FALSE #define Curl_ssl_cfilter_add(a, b, c) CURLE_NOT_BUILT_IN diff --git a/vendor/curl/lib/vtls/vtls_int.h b/vendor/curl/lib/vtls/vtls_int.h index ec72b64..6700ee7 100644 --- a/vendor/curl/lib/vtls/vtls_int.h +++ b/vendor/curl/lib/vtls/vtls_int.h @@ -23,19 +23,22 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" - -#include "../cfilters.h" -#include "../select.h" -#include "../urldata.h" -#include "vtls.h" +#include "curl_setup.h" #ifdef USE_SSL +#include "cfilters.h" +#include "select.h" +#include "urldata.h" +#include "vtls/vtls.h" + struct Curl_ssl; struct ssl_connect_data; +struct Curl_ssl_session; /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ +#define ALPN_HTTP_1_0_LENGTH 8 +#define ALPN_HTTP_1_0 "http/1.0" #define ALPN_HTTP_1_1_LENGTH 8 #define ALPN_HTTP_1_1 "http/1.1" #define ALPN_H2_LENGTH 2 @@ -99,12 +102,12 @@ typedef enum { ssl_earlydata_rejected } ssl_earlydata_state; -#define CURL_SSL_IO_NEED_NONE (0) +#define CURL_SSL_IO_NEED_NONE 0 #define CURL_SSL_IO_NEED_RECV (1 << 0) #define CURL_SSL_IO_NEED_SEND (1 << 1) /* Max earlydata payload we want to send */ -#define CURL_SSL_EARLY_MAX (64 * 1024) +#define CURL_SSL_EARLY_MAX (64 * 1024) /* Information in each SSL cfilter context: cf->ctx */ struct ssl_connect_data { @@ -198,6 +201,11 @@ CURLcode Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, */ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf); +CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data, bool early_data_allowed); #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */ diff --git a/vendor/curl/lib/vtls/vtls_scache.c b/vendor/curl/lib/vtls/vtls_scache.c index df04ec7..673abcf 100644 --- a/vendor/curl/lib/vtls/vtls_scache.c +++ b/vendor/curl/lib/vtls/vtls_scache.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SSL @@ -29,25 +29,23 @@ #include #endif -#include "../urldata.h" -#include "../cfilters.h" +#include "urldata.h" +#include "cfilters.h" -#include "vtls.h" /* generic SSL protos etc */ -#include "vtls_int.h" -#include "vtls_scache.h" -#include "vtls_spack.h" +#include "vtls/vtls.h" /* generic SSL protos etc */ +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/vtls_spack.h" -#include "../strcase.h" -#include "../url.h" -#include "../llist.h" -#include "../curl_share.h" -#include "../curl_trc.h" -#include "../curl_sha256.h" -#include "../rand.h" +#include "strcase.h" +#include "url.h" +#include "llist.h" +#include "curl_share.h" +#include "curl_trc.h" +#include "curl_sha256.h" +#include "rand.h" -static bool cf_ssl_peer_key_is_global(const char *peer_key); - /* a peer+tls-config we cache sessions for */ struct Curl_ssl_scache_peer { char *ssl_peer_key; /* id for peer + relevant TLS configuration */ @@ -60,7 +58,7 @@ struct Curl_ssl_scache_peer { unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */ unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */ size_t max_sessions; - long age; /* just a number, the higher the more recent */ + long age; /* a number, the higher the more recent */ BIT(hmac_set); /* if key_salt and key_hmac are present */ BIT(exportable); /* sessions for this peer can be exported */ }; @@ -69,6 +67,234 @@ struct Curl_ssl_scache_peer { #define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC) +static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, + const char *name, + const char *path, + bool *is_local) +{ + if(path && path[0]) { + /* We try to add absolute paths, so that the session key can stay valid + * when used in another process with different CWD. When a path does not + * exist, this does not work. Then, we add the path as is. */ +#ifdef _WIN32 + char abspath[_MAX_PATH]; + if(_fullpath(abspath, path, _MAX_PATH)) + return curlx_dyn_addf(buf, ":%s-%s", name, abspath); + *is_local = TRUE; +#elif defined(HAVE_REALPATH) + if(path[0] != '/') { + char *abspath = realpath(path, NULL); + if(abspath) { + CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath); + /* !checksrc! disable BANNEDFUNC 1 */ + free(abspath); /* allocated by libc, free without memdebug */ + return r; + } + *is_local = TRUE; + } +#endif + return curlx_dyn_addf(buf, ":%s-%s", name, path); + } + return CURLE_OK; +} + +static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, + const char *name, + struct curl_blob *blob) +{ + CURLcode r = CURLE_OK; + if(blob && blob->len) { + unsigned char hash[CURL_SHA256_DIGEST_LENGTH]; + size_t i; + + r = curlx_dyn_addf(buf, ":%s-", name); + if(r) + goto out; + r = Curl_sha256it(hash, blob->data, blob->len); + if(r) + goto out; + for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) { + r = curlx_dyn_addf(buf, "%02x", hash[i]); + if(r) + goto out; + } + } +out: + return r; +} + +#define CURL_SSLS_LOCAL_SUFFIX ":L" +#define CURL_SSLS_GLOBAL_SUFFIX ":G" + +static bool cf_ssl_peer_key_is_global(const char *peer_key) +{ + size_t len = peer_key ? strlen(peer_key) : 0; + return (len > 2) && + (peer_key[len - 1] == 'G') && + (peer_key[len - 2] == ':'); +} + +CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, + const struct ssl_peer *peer, + const char *tls_id, + char **ppeer_key) +{ + struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); + struct dynbuf buf; + size_t key_len; + bool is_local = FALSE; + CURLcode r; + + *ppeer_key = NULL; + curlx_dyn_init(&buf, 10 * 1024); + + r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); + if(r) + goto out; + + switch(peer->transport) { + case TRNSPRT_TCP: + break; + case TRNSPRT_UDP: + r = curlx_dyn_add(&buf, ":UDP"); + break; + case TRNSPRT_QUIC: + r = curlx_dyn_add(&buf, ":QUIC"); + break; + case TRNSPRT_UNIX: + r = curlx_dyn_add(&buf, ":UNIX"); + break; + default: + r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); + break; + } + if(r) + goto out; + + if(!ssl->verifypeer) { + r = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); + if(r) + goto out; + } + if(!ssl->verifyhost) { + r = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); + if(r) + goto out; + } + if(ssl->verifystatus) { + r = curlx_dyn_add(&buf, ":VRFY-STATUS"); + if(r) + goto out; + } + if(!ssl->verifypeer || !ssl->verifyhost) { + if(cf->conn->bits.conn_to_host) { + r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); + if(r) + goto out; + } + if(cf->conn->bits.conn_to_port) { + r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); + if(r) + goto out; + } + } + + if(ssl->version || ssl->version_max) { + r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version, + (ssl->version_max >> 16)); + if(r) + goto out; + } + if(ssl->ssl_options) { + r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); + if(r) + goto out; + } + if(ssl->cipher_list) { + r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); + if(r) + goto out; + } + if(ssl->cipher_list13) { + r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); + if(r) + goto out; + } + if(ssl->curves) { + r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); + if(r) + goto out; + } + if(ssl->verifypeer) { + r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); + if(r) + goto out; + r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local); + if(r) + goto out; + if(ssl->cert_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); + if(r) + goto out; + } + if(ssl->ca_info_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); + if(r) + goto out; + } + if(ssl->issuercert_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob); + if(r) + goto out; + } + } + if(ssl->pinned_key && ssl->pinned_key[0]) { + r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); + if(r) + goto out; + } + + if(ssl->clientcert && ssl->clientcert[0]) { + r = curlx_dyn_add(&buf, ":CCERT"); + if(r) + goto out; + } +#ifdef USE_TLS_SRP + if(ssl->username || ssl->password) { + r = curlx_dyn_add(&buf, ":SRP-AUTH"); + if(r) + goto out; + } +#endif + + if(!tls_id || !tls_id[0]) { + r = CURLE_FAILED_INIT; + goto out; + } + r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); + if(r) + goto out; + + r = curlx_dyn_addf(&buf, is_local ? + CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); + if(r) + goto out; + + *ppeer_key = curlx_dyn_take(&buf, &key_len); + /* we added printable char, and dynbuf always null-terminates, no need + * to track length */ + +out: + curlx_dyn_free(&buf); + return r; +} + struct Curl_ssl_scache { unsigned int magic; struct Curl_ssl_scache_peer *peers; @@ -334,7 +560,7 @@ CURLcode Curl_ssl_scache_create(size_t max_peers, void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache) { - if(scache && GOOD_SCACHE(scache)) { + if(GOOD_SCACHE(scache)) { size_t i; scache->magic = 0; for(i = 0; i < scache->peer_count; ++i) { @@ -368,235 +594,6 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data) Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); } -static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, - const char *name, - const char *path, - bool *is_local) -{ - if(path && path[0]) { - /* We try to add absolute paths, so that the session key can stay - * valid when used in another process with different CWD. However, - * when a path does not exist, this does not work. Then, we add - * the path as is. */ -#ifdef _WIN32 - char abspath[_MAX_PATH]; - if(_fullpath(abspath, path, _MAX_PATH)) - return curlx_dyn_addf(buf, ":%s-%s", name, abspath); - *is_local = TRUE; -#elif defined(HAVE_REALPATH) - if(path[0] != '/') { - char *abspath = realpath(path, NULL); - if(abspath) { - CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath); - /* !checksrc! disable BANNEDFUNC 1 */ - free(abspath); /* allocated by libc, free without memdebug */ - return r; - } - *is_local = TRUE; - } -#endif - return curlx_dyn_addf(buf, ":%s-%s", name, path); - } - return CURLE_OK; -} - -static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, - const char *name, - struct curl_blob *blob) -{ - CURLcode r = CURLE_OK; - if(blob && blob->len) { - unsigned char hash[CURL_SHA256_DIGEST_LENGTH]; - size_t i; - - r = curlx_dyn_addf(buf, ":%s-", name); - if(r) - goto out; - r = Curl_sha256it(hash, blob->data, blob->len); - if(r) - goto out; - for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) { - r = curlx_dyn_addf(buf, "%02x", hash[i]); - if(r) - goto out; - } - } -out: - return r; -} - -#define CURL_SSLS_LOCAL_SUFFIX ":L" -#define CURL_SSLS_GLOBAL_SUFFIX ":G" - -static bool cf_ssl_peer_key_is_global(const char *peer_key) -{ - size_t len = peer_key ? strlen(peer_key) : 0; - return (len > 2) && - (peer_key[len - 1] == 'G') && - (peer_key[len - 2] == ':'); -} - -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, - const char *tls_id, - char **ppeer_key) -{ - struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); - struct dynbuf buf; - size_t key_len; - bool is_local = FALSE; - CURLcode r; - - *ppeer_key = NULL; - curlx_dyn_init(&buf, 10 * 1024); - - r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); - if(r) - goto out; - - switch(peer->transport) { - case TRNSPRT_TCP: - break; - case TRNSPRT_UDP: - r = curlx_dyn_add(&buf, ":UDP"); - break; - case TRNSPRT_QUIC: - r = curlx_dyn_add(&buf, ":QUIC"); - break; - case TRNSPRT_UNIX: - r = curlx_dyn_add(&buf, ":UNIX"); - break; - default: - r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); - break; - } - if(r) - goto out; - - if(!ssl->verifypeer) { - r = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); - if(r) - goto out; - } - if(!ssl->verifyhost) { - r = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); - if(r) - goto out; - } - if(ssl->verifystatus) { - r = curlx_dyn_add(&buf, ":VRFY-STATUS"); - if(r) - goto out; - } - if(!ssl->verifypeer || !ssl->verifyhost) { - if(cf->conn->bits.conn_to_host) { - r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); - if(r) - goto out; - } - if(cf->conn->bits.conn_to_port) { - r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); - if(r) - goto out; - } - } - - if(ssl->version || ssl->version_max) { - r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version, - (ssl->version_max >> 16)); - if(r) - goto out; - } - if(ssl->ssl_options) { - r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); - if(r) - goto out; - } - if(ssl->cipher_list) { - r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); - if(r) - goto out; - } - if(ssl->cipher_list13) { - r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); - if(r) - goto out; - } - if(ssl->curves) { - r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); - if(r) - goto out; - } - if(ssl->verifypeer) { - r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); - if(r) - goto out; - r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); - if(r) - goto out; - r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); - if(r) - goto out; - r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local); - if(r) - goto out; - if(ssl->cert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); - if(r) - goto out; - } - if(ssl->ca_info_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); - if(r) - goto out; - } - if(ssl->issuercert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob); - if(r) - goto out; - } - } - if(ssl->pinned_key && ssl->pinned_key[0]) { - r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); - if(r) - goto out; - } - - if(ssl->clientcert && ssl->clientcert[0]) { - r = curlx_dyn_add(&buf, ":CCERT"); - if(r) - goto out; - } -#ifdef USE_TLS_SRP - if(ssl->username || ssl->password) { - r = curlx_dyn_add(&buf, ":SRP-AUTH"); - if(r) - goto out; - } -#endif - - if(!tls_id || !tls_id[0]) { - r = CURLE_FAILED_INIT; - goto out; - } - r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); - if(r) - goto out; - - r = curlx_dyn_addf(&buf, is_local ? - CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); - if(r) - goto out; - - *ppeer_key = curlx_dyn_take(&buf, &key_len); - /* we just added printable char, and dynbuf always null-terminates, no need - * to track length */ - -out: - curlx_dyn_free(&buf); - return r; -} - static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, struct ssl_primary_config *conn_config) { @@ -916,7 +913,7 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, void *sobj, - Curl_ssl_scache_obj_dtor *sobj_free) + Curl_ssl_scache_obj_dtor *sobj_dtor_cb) { struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -924,7 +921,7 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, CURLcode result; DEBUGASSERT(sobj); - DEBUGASSERT(sobj_free); + DEBUGASSERT(sobj_dtor_cb); if(!scache) { result = CURLE_BAD_FUNCTION_ARGUMENT; @@ -937,12 +934,12 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, goto out; } - cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free); + cf_ssl_scache_peer_set_obj(peer, sobj, sobj_dtor_cb); sobj = NULL; /* peer took ownership */ out: - if(sobj && sobj_free) - sobj_free(sobj); + if(sobj && sobj_dtor_cb) + sobj_dtor_cb(sobj); return result; } @@ -1150,9 +1147,12 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, struct Curl_ssl_scache_peer *peer; struct dynbuf sbuf, hbuf; struct Curl_llist_node *n; - size_t i, npeers = 0, ntickets = 0; + size_t i; curl_off_t now = time(NULL); CURLcode r = CURLE_OK; +#ifdef CURLVERBOSE + size_t npeers = 0, ntickets = 0; +#endif if(!export_fn) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1175,7 +1175,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, cf_scache_peer_remove_expired(peer, now); n = Curl_llist_head(&peer->sessions); if(n) - ++npeers; + VERBOSE(++npeers); while(n) { struct Curl_ssl_session *s = Curl_node_elem(n); if(!peer->hmac_set) { @@ -1203,10 +1203,9 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, s->alpn, s->earlydata_max); if(r) goto out; - ++ntickets; + VERBOSE(++ntickets); n = Curl_node_next(n); } - } r = CURLE_OK; CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers", diff --git a/vendor/curl/lib/vtls/vtls_scache.h b/vendor/curl/lib/vtls/vtls_scache.h index 2a99e77..b9db0f1 100644 --- a/vendor/curl/lib/vtls/vtls_scache.h +++ b/vendor/curl/lib/vtls/vtls_scache.h @@ -23,13 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" - -#include "../cfilters.h" -#include "../urldata.h" +#include "curl_setup.h" #ifdef USE_SSL +#include "cfilters.h" +#include "urldata.h" + struct Curl_cfilter; struct Curl_easy; struct Curl_ssl_scache; diff --git a/vendor/curl/lib/vtls/vtls_spack.c b/vendor/curl/lib/vtls/vtls_spack.c index 9eeb808..d96f4e4 100644 --- a/vendor/curl/lib/vtls/vtls_spack.c +++ b/vendor/curl/lib/vtls/vtls_spack.c @@ -21,15 +21,15 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) -#include "../urldata.h" -#include "../curl_trc.h" -#include "vtls_scache.h" -#include "vtls_spack.h" -#include "../strdup.h" +#include "urldata.h" +#include "curl_trc.h" +#include "vtls/vtls_scache.h" +#include "vtls/vtls_spack.h" +#include "curlx/strdup.h" #ifndef UINT16_MAX #define UINT16_MAX 0xffff @@ -152,7 +152,7 @@ static CURLcode spack_decstr16(char **val, const uint8_t **src, return r; if(end - *src < slen) return CURLE_READ_ERROR; - *val = Curl_memdup0((const char *)(*src), slen); + *val = curlx_memdup0((const char *)(*src), slen); *src += slen; return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY; } @@ -182,7 +182,7 @@ static CURLcode spack_decdata16(uint8_t **val, size_t *val_len, return r; if(end - *src < data_len) return CURLE_READ_ERROR; - *val = Curl_memdup0((const char *)(*src), data_len); + *val = curlx_memdup0((const char *)(*src), data_len); *val_len = data_len; *src += data_len; return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY; diff --git a/vendor/curl/lib/vtls/vtls_spack.h b/vendor/curl/lib/vtls/vtls_spack.h index 86796ee..4479384 100644 --- a/vendor/curl/lib/vtls/vtls_spack.h +++ b/vendor/curl/lib/vtls/vtls_spack.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) diff --git a/vendor/curl/lib/vtls/wolfssl.c b/vendor/curl/lib/vtls/wolfssl.c index 5103788..aa841a7 100644 --- a/vendor/curl/lib/vtls/wolfssl.c +++ b/vendor/curl/lib/vtls/wolfssl.c @@ -26,17 +26,19 @@ * but vtls.c should ever call or use these functions. * */ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_WOLFSSL -#define WOLFSSL_OPTIONS_IGNORE_SYS #include #include #if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */ #error "wolfSSL version should be at least 3.4.6" #endif +#if defined(OPENSSL_COEXIST) && LIBWOLFSSL_VERSION_HEX < 0x05007006 +#error "wolfSSL 5.7.6 or newer is required to coexist with OpenSSL" +#endif /* To determine what functions are available we rely on one or both of: - the user's options.h generated by wolfSSL @@ -51,21 +53,23 @@ #endif #endif -#include "../urldata.h" -#include "../curl_trc.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "keylog.h" -#include "../connect.h" /* for the connect timeout */ -#include "../progress.h" -#include "../strdup.h" -#include "../curlx/strcopy.h" -#include "x509asn1.h" +#include "urldata.h" +#include "curl_trc.h" +#include "httpsrr.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.h" +#include "vtls/vtls_scache.h" +#include "vtls/keylog.h" +#include "connect.h" /* for the connect timeout */ +#include "progress.h" +#include "curlx/strdup.h" +#include "curlx/strcopy.h" +#include "vtls/x509asn1.h" #include #include -#include "wolfssl.h" + +#include "vtls/wolfssl.h" /* KEEP_PEER_CERT is a product of the presence of build time symbol OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is @@ -100,10 +104,6 @@ #undef USE_BIO_CHAIN #endif -static CURLcode wssl_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done); - #ifdef OPENSSL_EXTRA /* * Availability note: @@ -325,7 +325,7 @@ static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif - if(CURLE_AGAIN == result) { + if(result == CURLE_AGAIN) { wolfSSL_BIO_set_retry_write(bio); if(wssl->shutting_down && !wssl->io_send_blocked_len) wssl->io_send_blocked_len = blen; @@ -370,7 +370,7 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) wolfSSL_BIO_set_retry_read(bio); else if(nread == 0) connssl->peer_closed = TRUE; @@ -401,7 +401,7 @@ static void wssl_bio_cf_free_methods(void) #else /* USE_BIO_CHAIN */ -#define wssl_bio_cf_init_methods() Curl_nop_stmt +#define wssl_bio_cf_init_methods() TRUE #define wssl_bio_cf_free_methods() Curl_nop_stmt #endif /* !USE_BIO_CHAIN */ @@ -446,7 +446,7 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, goto out; } if(quic_tp && quic_tp_len) { - qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len); + qtp_clone = curlx_memdup0((const char *)quic_tp, quic_tp_len); if(!qtp_clone) { curlx_free(sdata); return CURLE_OUT_OF_MEMORY; @@ -501,36 +501,18 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, bool *do_early_data) { struct ssl_connect_data *connssl = cf->ctx; +#ifdef WOLFSSL_EARLY_DATA struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; - CURLcode result = CURLE_OK; - *do_early_data = FALSE; -#ifdef WOLFSSL_EARLY_DATA connssl->earlydata_max = wolfSSL_SESSION_get_max_early_data( wolfSSL_get_session(wssl->ssl)); #else - (void)wssl; connssl->earlydata_max = 0; #endif - if(!connssl->earlydata_max) { - /* Seems to be no WolfSSL way to signal no EarlyData in session */ - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data"); - } - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); - connssl->earlydata_state = ssl_earlydata_await; - connssl->state = ssl_connection_deferred; - result = Curl_alpn_set_negotiated(cf, data, connssl, - (const unsigned char *)scs->alpn, - scs->alpn ? strlen(scs->alpn) : 0); - *do_early_data = !result; - } - return result; + /* Seems to be no wolfSSL way to signal no EarlyData in session */ + return Curl_on_session_reuse(cf, data, alpns, scs, do_early_data, + connssl->earlydata_max); } static CURLcode @@ -676,7 +658,7 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, return CURLE_SSL_CACERT_BADFILE; } else { - /* Just continue with a warning if no strict certificate + /* continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:"); @@ -1031,8 +1013,9 @@ static CURLcode ssl_version(struct Curl_easy *data, { int res; *min_version = *max_version = 0; + DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT); + switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: *min_version = TLS1_VERSION; @@ -1155,7 +1138,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, #ifndef WOLFSSL_TLS13 { - char *ciphers = conn_config->cipher_list; + const char *ciphers = conn_config->cipher_list; if(ciphers) { if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); @@ -1444,7 +1427,6 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, result = CURLE_SSL_CONNECT_ERROR; goto out; } - } #endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ @@ -1630,7 +1612,7 @@ static CURLcode wssl_send_earlydata(struct Curl_cfilter *cf, int err = wolfSSL_get_error(wssl->ssl, rc); char error_buffer[256]; switch(err) { - case WOLFSSL_ERROR_NONE: /* just did not get anything */ + case WOLFSSL_ERROR_NONE: /* did not get anything */ case WOLFSSL_ERROR_WANT_READ: case WOLFSSL_ERROR_WANT_WRITE: return CURLE_AGAIN; @@ -1759,7 +1741,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, " CA signer not available for verification"); return CURLE_SSL_CACERT_BADFILE; } - /* Just continue with a warning if no strict certificate + /* Continue with a warning if no strict certificate verification is required. */ infof(data, "CA signer not available for verification, " "continuing anyway"); @@ -1888,9 +1870,9 @@ static CURLcode wssl_shutdown(struct Curl_cfilter *cf, struct wssl_ctx *wctx = (struct wssl_ctx *)connssl->backend; CURLcode result = CURLE_OK; char buf[1024]; - char error_buffer[256]; int nread = -1, err; size_t i; + VERBOSE(char error_buffer[256]); DEBUGASSERT(wctx); if(!wctx->ssl || cf->shutdown) { @@ -1959,7 +1941,7 @@ static CURLcode wssl_shutdown(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "SSL shutdown received"); *done = TRUE; break; - case WOLFSSL_ERROR_NONE: /* just did not get anything */ + case WOLFSSL_ERROR_NONE: /* did not get anything */ case WOLFSSL_ERROR_WANT_READ: /* wolfSSL has send its notify and now wants to read the reply * from the server. We are not really interested in that. */ @@ -2112,6 +2094,7 @@ static bool wssl_data_pending(struct Curl_cfilter *cf, void Curl_wssl_report_handshake(struct Curl_easy *data, struct wssl_ctx *wssl) { + (void)wssl; #if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) infof(data, "SSL connection using %s / %s", wolfSSL_get_version(wssl->ssl), @@ -2286,7 +2269,8 @@ const struct Curl_ssl Curl_ssl_wolfssl = { SSLSUPP_TLS13_CIPHERSUITES | #endif SSLSUPP_CA_CACHE | - SSLSUPP_CIPHER_LIST, + SSLSUPP_CIPHER_LIST | + SSLSUPP_SSL_EC_CURVES, sizeof(struct wssl_ctx), diff --git a/vendor/curl/lib/vtls/wolfssl.h b/vendor/curl/lib/vtls/wolfssl.h index 736da9a..b097b60 100644 --- a/vendor/curl/lib/vtls/wolfssl.h +++ b/vendor/curl/lib/vtls/wolfssl.h @@ -23,11 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_WOLFSSL -#include "../urldata.h" +#include "urldata.h" struct alpn_spec; struct ssl_peer; diff --git a/vendor/curl/lib/vtls/x509asn1.c b/vendor/curl/lib/vtls/x509asn1.c index e6fdb10..b687a60 100644 --- a/vendor/curl/lib/vtls/x509asn1.c +++ b/vendor/curl/lib/vtls/x509asn1.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ defined(USE_MBEDTLS) || defined(USE_RUSTLS) @@ -36,12 +36,12 @@ #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ #endif -#include "../urldata.h" -#include "vtls.h" -#include "../curl_trc.h" -#include "../curlx/base64.h" -#include "x509asn1.h" -#include "../curlx/dynbuf.h" +#include "urldata.h" +#include "vtls/vtls.h" +#include "curl_trc.h" +#include "curlx/base64.h" +#include "vtls/x509asn1.h" +#include "curlx/dynbuf.h" /* * Constants. @@ -50,6 +50,7 @@ /* Largest supported ASN.1 structure. */ #define CURL_ASN1_MAX ((size_t)0x40000) /* 256K */ +#ifdef WANT_EXTRACT_CERTINFO /* ASN.1 classes. */ /* #define CURL_ASN1_UNIVERSAL 0 */ /* #define CURL_ASN1_APPLICATION 1 */ @@ -86,7 +87,6 @@ /* #define CURL_ASN1_CHARACTER_STRING 29 */ #define CURL_ASN1_BMP_STRING 30 -#ifdef WANT_EXTRACT_CERTINFO /* ASN.1 OID table entry. */ struct Curl_OID { const char *numoid; /* Dotted-numeric OID. */ @@ -146,7 +146,7 @@ static const struct Curl_OID OIDtable[] = { { "2.16.840.1.101.3.4.2.2", "sha384" }, { "2.16.840.1.101.3.4.2.3", "sha512" }, { "1.2.840.113549.1.9.2", "unstructuredName" }, - { (const char *)NULL, (const char *)NULL } + { NULL, NULL } }; #endif /* WANT_EXTRACT_CERTINFO */ @@ -369,7 +369,7 @@ static CURLcode utf8asn1str(struct dynbuf *to, int type, const char *from, return CURLE_BAD_FUNCTION_ARGUMENT; if(type == CURL_ASN1_UTF8_STRING) { - /* Just copy. */ + /* copy. */ if(inlength) result = curlx_dyn_addn(to, from, inlength); } @@ -443,6 +443,8 @@ static CURLcode encodeOID(struct dynbuf *store, do { if(x & 0xFF000000) return CURLE_OK; + else if(beg == end) + return CURLE_BAD_FUNCTION_ARGUMENT; y = *(const unsigned char *)beg++; x = (x << 7) | (y & 0x7F); } while(y & 0x80); @@ -473,8 +475,8 @@ static CURLcode OID2str(struct dynbuf *store, result = curlx_dyn_add(store, op->textoid); else result = curlx_dyn_add(store, curlx_dyn_ptr(&buf)); - curlx_dyn_free(&buf); } + curlx_dyn_free(&buf); } else result = encodeOID(store, beg, end); @@ -556,7 +558,7 @@ static CURLcode GTime2str(struct dynbuf *store, "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", beg, beg + 4, beg + 6, beg + 8, beg + 10, sec1, sec2, - fracl ? ".": "", (int)fracl, fracp, + fracl ? "." : "", (int)fracl, fracp, sep, (int)tzl, tzp); } @@ -932,7 +934,7 @@ static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data, struct dynbuf *ptr) { size_t valuelen = curlx_dyn_len(ptr); - char *value = curlx_dyn_ptr(ptr); + const char *value = curlx_dyn_ptr(ptr); CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); @@ -979,11 +981,16 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, * ECC public key is all the data, a value of type BIT STRING mapped to * OCTET STRING and should not be parsed as an ASN.1 value. */ - const size_t len = ((pubkey->end - pubkey->beg - 2) * 4); + const size_t dlen = pubkey->end - pubkey->beg; + size_t len; + if(dlen < 2) + /* too small */ + return 1; + len = (dlen - 2) * 4; if(!certnum) infof(data, " ECC Public Key (%zu bits)", len); if(data->set.ssl.certinfo) { - char q[sizeof(len) * 8 / 3 + 1]; + char q[(sizeof(len) * 8 / 3) + 1]; (void)curl_msnprintf(q, sizeof(q), "%zu", len); if(ssl_push_certinfo(data, certnum, "ECC Public Key", q)) return 1; @@ -1018,7 +1025,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, if(!certnum) infof(data, " RSA Public Key (%zu bits)", len); if(data->set.ssl.certinfo) { - char r[sizeof(len) * 8 / 3 + 1]; + char r[(sizeof(len) * 8 / 3) + 1]; curl_msnprintf(r, sizeof(r), "%zu", len); if(ssl_push_certinfo(data, certnum, "RSA Public Key", r)) return 1; diff --git a/vendor/curl/lib/vtls/x509asn1.h b/vendor/curl/lib/vtls/x509asn1.h index ba2c389..829dfd8 100644 --- a/vendor/curl/lib/vtls/x509asn1.h +++ b/vendor/curl/lib/vtls/x509asn1.h @@ -24,13 +24,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ defined(USE_MBEDTLS) || defined(USE_RUSTLS) -#include "../cfilters.h" -#include "../urldata.h" +#include "cfilters.h" +#include "urldata.h" /* * Types. diff --git a/vendor/curl/lib/ws.c b/vendor/curl/lib/ws.c index 37cacae..6046561 100644 --- a/vendor/curl/lib/ws.c +++ b/vendor/curl/lib/ws.c @@ -22,10 +22,11 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" +#include "ws.h" -#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +#ifndef CURL_DISABLE_WEBSOCKETS -#include "urldata.h" #include "url.h" #include "bufq.h" #include "curlx/dynbuf.h" @@ -35,7 +36,6 @@ #include "sendf.h" #include "curl_trc.h" #include "multiif.h" -#include "ws.h" #include "easyif.h" #include "transfer.h" #include "select.h" @@ -52,18 +52,20 @@ |N|V|V|V| | | |1|2|3| | */ -#define WSBIT_FIN (0x80) -#define WSBIT_RSV1 (0x40) -#define WSBIT_RSV2 (0x20) -#define WSBIT_RSV3 (0x10) +#define WSBIT_FIN 0x80 +#define WSBIT_RSV1 0x40 +#define WSBIT_RSV2 0x20 +#define WSBIT_RSV3 0x10 #define WSBIT_RSV_MASK (WSBIT_RSV1 | WSBIT_RSV2 | WSBIT_RSV3) -#define WSBIT_OPCODE_CONT (0x0) -#define WSBIT_OPCODE_TEXT (0x1) -#define WSBIT_OPCODE_BIN (0x2) -#define WSBIT_OPCODE_CLOSE (0x8) -#define WSBIT_OPCODE_PING (0x9) -#define WSBIT_OPCODE_PONG (0xa) -#define WSBIT_OPCODE_MASK (0xf) +#define WSBIT_OPCODE_CONT 0x0 +#define WSBIT_OPCODE_TEXT 0x1 +#define WSBIT_OPCODE_BIN 0x2 +#define WSBIT_OPCODE_CLOSE 0x8 +#define WSBIT_OPCODE_PING 0x9 +#define WSBIT_OPCODE_PONG 0xa +#ifdef CURLVERBOSE +#define WSBIT_OPCODE_MASK 0xf +#endif #define WSBIT_MASK 0x80 @@ -123,6 +125,7 @@ struct websocket { size_t sendbuf_payload; /* number of payload bytes in sendbuf */ }; +#ifdef CURLVERBOSE static const char *ws_frame_name_of_op(uint8_t firstbyte) { switch(firstbyte & WSBIT_OPCODE_MASK) { @@ -142,6 +145,7 @@ static const char *ws_frame_name_of_op(uint8_t firstbyte) return "???"; } } +#endif static int ws_frame_firstbyte2flags(struct Curl_easy *data, uint8_t firstbyte, int cont_flags) @@ -288,6 +292,7 @@ static CURLcode ws_frame_flags2firstbyte(struct Curl_easy *data, static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, const char *msg) { + NOVERBOSE((void)msg); switch(dec->head_len) { case 0: break; @@ -378,7 +383,9 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, } dec->head_len = 1; - /* ws_dec_info(dec, data, "seeing opcode"); */ +#if 0 + ws_dec_info(dec, data, "seeing opcode"); +#endif continue; } else if(dec->head_len == 1) { @@ -430,7 +437,9 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, Curl_bufq_skip(inraw, 1); ++dec->head_len; if(dec->head_len < dec->head_total) { - /* ws_dec_info(dec, data, "decoding head"); */ +#if 0 + ws_dec_info(dec, data, "decoding head"); +#endif continue; } } @@ -445,7 +454,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, break; case 10: if(dec->head[2] > 127) { - failf(data, "[WS] frame length longer than 63 bit not supported"); + failf(data, "[WS] frame length longer than 63 bits not supported"); return CURLE_RECV_ERROR; } dec->payload_len = @@ -486,7 +495,6 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, size_t remain = curlx_sotouz_range(dec->payload_len - dec->payload_offset, 0, SIZE_MAX); - (void)data; while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen)) { if(inlen > remain) inlen = remain; @@ -614,7 +622,7 @@ static CURLcode ws_enc_send(struct Curl_easy *data, size_t buflen, curl_off_t fragsize, unsigned int flags, - size_t *sent); + size_t *pnsent); static CURLcode ws_enc_add_pending(struct Curl_easy *data, struct websocket *ws); @@ -771,6 +779,8 @@ static const struct Curl_cwtype ws_cw_decode = { static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, const char *msg) { + NOVERBOSE((void)enc); + NOVERBOSE((void)msg); CURL_TRC_WS(data, "WS-ENC: %s [%s%s payload=%" FMT_OFF_T "/%" FMT_OFF_T "]", msg, ws_frame_name_of_op(enc->firstbyte), @@ -837,14 +847,15 @@ static CURLcode ws_enc_add_frame(struct Curl_easy *data, return CURLE_SEND_ERROR; } - result = ws_frame_flags2firstbyte(data, flags, enc->contfragment, &firstb); + result = ws_frame_flags2firstbyte(data, flags, (bool)enc->contfragment, + &firstb); if(result) return result; /* fragmentation only applies to data frames (text/binary); * control frames (close/ping/pong) do not affect the CONT status */ if(flags & (CURLWS_TEXT | CURLWS_BINARY)) { - enc->contfragment = (flags & CURLWS_CONT) ? (bit)TRUE : (bit)FALSE; + enc->contfragment = (curl_bit)((flags & CURLWS_CONT) ? TRUE : FALSE); } if(flags & CURLWS_PING && payload_len > WS_MAX_CNTRL_LEN) { @@ -1178,7 +1189,7 @@ static CURLcode cr_ws_read(struct Curl_easy *data, if(ctx->read_eos) ctx->eos = TRUE; *pnread = nread; - *peos = ctx->eos; + *peos = (bool)ctx->eos; goto out; } @@ -1693,7 +1704,7 @@ static CURLcode ws_send_raw_blocking(struct Curl_easy *data, CURL_TRC_WS(data, "ws_send_raw_blocking() partial, %zu left to send", buflen); - left_ms = Curl_timeleft_ms(data, FALSE); + left_ms = Curl_timeleft_ms(data); if(left_ms < 0) { failf(data, "[WS] Timeout waiting for socket becoming writable"); return CURLE_SEND_ERROR; @@ -1904,8 +1915,7 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *d, return result; } -const struct Curl_handler Curl_handler_ws = { - "WS", /* scheme */ +static const struct Curl_protocol Curl_protocol_ws = { ws_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ @@ -1923,41 +1933,8 @@ const struct Curl_handler Curl_handler_ws = { ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ Curl_http_follow, /* follow */ - PORT_HTTP, /* defport */ - CURLPROTO_WS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL }; -#ifdef USE_SSL -const struct Curl_handler Curl_handler_wss = { - "WSS", /* scheme */ - ws_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - NULL, /* connecting */ - ZERO_NULL, /* doing */ - NULL, /* proto_pollset */ - Curl_http_doing_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - Curl_http_perform_pollset, /* perform_pollset */ - ZERO_NULL, /* disconnect */ - Curl_http_write_resp, /* write_resp */ - Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - Curl_http_follow, /* follow */ - PORT_HTTPS, /* defport */ - CURLPROTO_WSS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL -}; -#endif - #else CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, @@ -2002,4 +1979,33 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, return CURLE_NOT_BUILT_IN; } -#endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_WEBSOCKETS */ + +const struct Curl_scheme Curl_scheme_ws = { + "WS", /* scheme */ +#ifdef CURL_DISABLE_WEBSOCKETS + ZERO_NULL, +#else + &Curl_protocol_ws, +#endif + CURLPROTO_WS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL, + PORT_HTTP /* defport */ +} +; + +const struct Curl_scheme Curl_scheme_wss = { + "WSS", /* scheme */ +#if defined(CURL_DISABLE_WEBSOCKETS) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_ws, +#endif + CURLPROTO_WSS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL, + PORT_HTTPS /* defport */ +}; diff --git a/vendor/curl/lib/ws.h b/vendor/curl/lib/ws.h index a6e7706..757eae0 100644 --- a/vendor/curl/lib/ws.h +++ b/vendor/curl/lib/ws.h @@ -25,18 +25,17 @@ ***************************************************************************/ #include "curl_setup.h" +extern const struct Curl_scheme Curl_scheme_ws; +extern const struct Curl_scheme Curl_scheme_wss; + #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) /* meta key for storing protocol meta at connection */ #define CURL_META_PROTO_WS_CONN "meta:proto:ws:conn" CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req); -CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len); - -extern const struct Curl_handler Curl_handler_ws; -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_wss; -#endif +CURLcode Curl_ws_accept(struct Curl_easy *data, + const char *mem, size_t nread); #else #define Curl_ws_request(x, y) CURLE_OK From 6a1611c989b08f041e22ad07f7c507afc998775e Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Fri, 13 Mar 2026 12:04:33 -0400 Subject: [PATCH 2/2] Fix Signed-off-by: Juan Cruz Viotti --- .../0001-fix-schannel-alpn-init-order.patch | 49 +++++++++++++++++++ vendor/curl/lib/easy.c | 10 ++-- 2 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 patches/curl/0001-fix-schannel-alpn-init-order.patch diff --git a/patches/curl/0001-fix-schannel-alpn-init-order.patch b/patches/curl/0001-fix-schannel-alpn-init-order.patch new file mode 100644 index 0000000..142cd4b --- /dev/null +++ b/patches/curl/0001-fix-schannel-alpn-init-order.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Juan Cruz Viotti +Date: Thu, 13 Mar 2026 00:00:00 +0000 +Subject: [PATCH] Fix Schannel ALPN init order on Windows + +Curl_ssl_init() is called before Curl_win32_init() in global_init(). +The new curlx_verify_windows_version() requires curlx_verify_windows_init() +to be called first (from Curl_win32_init), but schannel_init() (called via +Curl_ssl_init) uses curlx_verify_windows_version() to check ALPN support. +Without the init, it falls back to VerifyVersionInfoW which lies about the +OS version on Windows 10+ without a compatibility manifest, causing the +ALPN check for >= 6.3 to fail and disabling HTTP/2. + +Move Curl_win32_init() before Curl_ssl_init() so the version function +pointer is set up before Schannel needs it. +--- + lib/easy.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/lib/easy.c b/lib/easy.c +index d05674e..a74d1da 100644 +--- a/lib/easy.c ++++ b/lib/easy.c +@@ -140,6 +140,11 @@ static CURLcode global_init(long flags, bool memoryfuncs) + goto fail; + } + ++ if(Curl_win32_init(flags)) { ++ DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n")); ++ goto fail; ++ } ++ + if(!Curl_ssl_init()) { + DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssl_init failed\n")); + goto fail; +@@ -150,11 +155,6 @@ static CURLcode global_init(long flags, bool memoryfuncs) + goto fail; + } + +- if(Curl_win32_init(flags)) { +- DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n")); +- goto fail; +- } +- + if(Curl_amiga_init()) { + DEBUGF(curl_mfprintf(stderr, "Error: Curl_amiga_init failed\n")); + goto fail; +-- +2.47.1 diff --git a/vendor/curl/lib/easy.c b/vendor/curl/lib/easy.c index d05674e..a74d1da 100644 --- a/vendor/curl/lib/easy.c +++ b/vendor/curl/lib/easy.c @@ -140,6 +140,11 @@ static CURLcode global_init(long flags, bool memoryfuncs) goto fail; } + if(Curl_win32_init(flags)) { + DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n")); + goto fail; + } + if(!Curl_ssl_init()) { DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssl_init failed\n")); goto fail; @@ -150,11 +155,6 @@ static CURLcode global_init(long flags, bool memoryfuncs) goto fail; } - if(Curl_win32_init(flags)) { - DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n")); - goto fail; - } - if(Curl_amiga_init()) { DEBUGF(curl_mfprintf(stderr, "Error: Curl_amiga_init failed\n")); goto fail;