diff options
-rw-r--r-- | .github/workflows/linux.yml | 18 | ||||
-rw-r--r-- | Dockerfile | 2 | ||||
-rw-r--r-- | RELEASE-NOTES | 49 | ||||
-rw-r--r-- | docs/examples/cacertinmem.c | 28 | ||||
-rw-r--r-- | docs/examples/usercertinmem.c | 52 | ||||
-rw-r--r-- | docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md | 15 | ||||
-rw-r--r-- | docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md | 19 | ||||
-rw-r--r-- | lib/multi.c | 42 | ||||
-rw-r--r-- | lib/sendf.c | 4 | ||||
-rw-r--r-- | lib/socks.c | 208 | ||||
-rw-r--r-- | lib/vssh/wolfssh.c | 288 | ||||
-rw-r--r-- | lib/vtls/mbedtls.c | 39 | ||||
-rw-r--r-- | lib/vtls/openssl.c | 35 | ||||
-rw-r--r-- | tests/data/Makefile.am | 6 | ||||
-rw-r--r-- | tests/data/test2307 | 60 | ||||
-rw-r--r-- | tests/data/test758 | 53 | ||||
-rwxr-xr-x | tests/ftpserver.pl | 7 | ||||
-rw-r--r-- | tests/libtest/Makefile.inc | 2 | ||||
-rw-r--r-- | tests/libtest/lib758.c | 515 | ||||
-rw-r--r-- | tests/processhelp.pm | 47 | ||||
-rw-r--r-- | tests/runner.pm | 8 | ||||
-rwxr-xr-x | tests/runtests.pl | 3 | ||||
-rw-r--r-- | tests/serverhelp.pm | 27 | ||||
-rw-r--r-- | tests/servers.pm | 2 |
24 files changed, 1092 insertions, 437 deletions
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 7f1ea5a5e..b479b46e4 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -103,7 +103,12 @@ jobs: - name: 'mbedtls valgrind' install_packages: libnghttp2-dev libidn2-dev libldap-dev valgrind install_steps: mbedtls - configure: LDFLAGS=-Wl,-rpath,/home/runner/mbedtls/lib --with-mbedtls=/home/runner/mbedtls --enable-debug + generate: >- + -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON + -DMBEDTLS_INCLUDE_DIR=/home/runner/mbedtls/include + -DMBEDTLS_LIBRARY=/home/runner/mbedtls/lib/libmbedtls.a + -DMBEDX509_LIBRARY=/home/runner/mbedtls/lib/libmbedx509.a + -DMBEDCRYPTO_LIBRARY=/home/runner/mbedtls/lib/libmbedcrypto.a - name: 'mbedtls clang' install_packages: libnghttp2-dev libldap-dev clang @@ -145,7 +150,7 @@ jobs: - name: 'openssl libssh2 sync-resolver valgrind' install_packages: zlib1g-dev libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind - configure: --with-openssl --enable-debug --disable-threaded-resolver --with-libssh2 + generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_USE_LIBSSH2=ON - name: 'openssl' install_packages: zlib1g-dev @@ -160,7 +165,8 @@ jobs: - name: 'openssl -O3 libssh valgrind' install_packages: zlib1g-dev libssh-dev valgrind - configure: CFLAGS=-O3 --with-openssl --enable-debug --with-libssh + CFLAGS: -O3 + generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON - name: 'openssl clang krb5 openldap static' install_steps: openldap-static @@ -260,12 +266,12 @@ jobs: - name: 'rustls valgrind' install_packages: libnghttp2-dev libldap-dev valgrind install_steps: rust rustls - configure: --with-rustls --enable-ech --enable-debug + generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - name: 'rustls' install_packages: libnghttp2-dev libldap-dev install_steps: rust rustls skiprun pytest - generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON + configure: --with-rustls --enable-ech --enable-debug - name: 'IntelC openssl' install_packages: zlib1g-dev libssl-dev @@ -700,7 +706,7 @@ jobs: ../.github/scripts/randcurl.pl 60 ../bld/src/curl - name: 'build examples' - if: ${{ matrix.build.make-custom-target != 'tidy' }} + if: ${{ !contains(matrix.build.install_packages, 'valgrind') && matrix.build.make-custom-target != 'tidy' }} run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then ${MATRIX_MAKE_PREFIX} cmake --build bld --verbose --target curl-examples-build diff --git a/Dockerfile b/Dockerfile index b4507ff67..67bd0664a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ # $ ./scripts/maketgz 8.7.1 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags -FROM debian:bookworm-slim@sha256:135c31f331d2c233a0035301460624091facfe097bdc3b3065f59ede9ad2f937 +FROM debian:bookworm-slim@sha256:b1a741487078b369e78119849663d7f1a5341ef2768798f7b7406c4240f86aef RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 9aa5003d0..ce492391b 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -3,8 +3,8 @@ curl and libcurl 8.15.1 Public curl releases: 270 Command line options: 272 curl_easy_setopt() options: 308 - Public functions in libcurl: 97 - Contributors: 3486 + Public functions in libcurl: 98 + Contributors: 3488 This release includes the following changes: @@ -15,6 +15,7 @@ This release includes the following changes: o curl: make --retry-delay and --retry-max-time accept decimal seconds [112] o hostip: cache negative name resolves [175] o ip happy eyeballing: keep attempts running [80] + o mbedtls: bump minimum version required to 3.2.0 [180] o multi: add curl_multi_get_offt [56] o multi: add CURLMOPT_NETWORK_CHANGED to signal network changed [84] o netrc: use the NETRC environment variable (first) if set [70] @@ -22,6 +23,7 @@ This release includes the following changes: o tls: make default TLS version be minimum 1.2 [71] o tool_getparam: add support for `--longopt=value` [69] o vquic: drop msh3 [8] + o websocket: support CURLOPT_READFUNCTION [193] o writeout: add %time{} [74] This release includes the following bugfixes: @@ -50,6 +52,7 @@ This release includes the following bugfixes: o cmake: fix `ENABLE_UNIX_SOCKETS=OFF` with pre-fill enabled on unix o cmake: fix to disable Schannel and SSPI for non-Windows targets o cmake: fix to restrict `SystemConfiguration` to macOS [139] + o cmake: improve error message for invalid HTTP/3 MultiSSL configs [187] o cmake: keep websockets disabled if HTTP is disabled o cmake: make `runtests` targets build the curl tool [32] o cmake: make the ExternalProject test work [183] @@ -62,6 +65,7 @@ This release includes the following bugfixes: o connectdata: remove primary+secondary ip_quadruple [126] o connection: terminate after goaway [62] o contrithanks: fix for BSD `sed` tool [98] + o cookie: don't treat the leading slash as trailing [185] o curl-config: remove X prefix use [138] o curl/system.h: fix for GCC 3.3.x and older [38] o curl: make the URL indexes 64 bit [117] @@ -84,6 +88,7 @@ This release includes the following bugfixes: o CURLOPT: bump remaining macros to `long` [147] o CURLOPT: drop redundant `long` casts [55] o CURLOPT: replace `(long)` cast with `L` suffix for `CURLHSTS_*` macros + o CURLOPT_HTTP_VERSION: mention new default value [179] o delta: fix warnings, fix for non-GNU `date` tool [99] o DEPRECATE.md: drop support for Windows XP/2003 [31] o DEPRECATE.md: remove leftover "nothing" [57] @@ -94,6 +99,8 @@ This release includes the following bugfixes: o doh: rename symbols to avoid collision with mingw-w64 headers [66] o easy handle: check validity on external calls [28] o examples: drop long cast for `CURLALTSVC_*` + o examples: remove base64 encoded chunks from examples [189] + o examples: remove href_extractor.c [186] o gnutls: some small cleanups [41] o hmac: return error if init fails [2] o hostip: do DNS cache pruning in milliseconds [132] @@ -109,6 +116,7 @@ This release includes the following bugfixes: o libtests: update format strings to avoid casts, drop some macros [109] o libtests: use `FMT_SOCKET_T`, drop more casts [136] o managen: reset text mode at end of table marker [145] + o mbedtls: check for feature macros instead of version [166] o memanalyze: fix warnings [22] o memory: make function overrides work reliably in unity builds [93] o multi event: remove only announced [25] @@ -122,13 +130,17 @@ This release includes the following bugfixes: o openssl: check SSL_write() length on retries [152] o openssl: clear errors after a failed `d2i_X509()` [161] o openssl: output unescaped utf8 x509 issuer/subject DNs [169] + o openssl: save and restore OpenSSL error queue in two functions [172] o openssl: some small cleanups [42] o openssl: split cert_stuff into smaller sub functions [72] o parallel-max: bump the max value to 65535 [86] + o processhelp.pm: fix to use the correct null device on Windows [164] o pytest: add SOCKS tests and scoring [9] o pytest: increase server KeepAliveTimeout [26] o pytest: relax error check on test_07_22 [16] + o resolving: dns error tracing [196] o runtests: add `--ci` option, show `Env:` only when non-empty [134] + o runtests: assume `Time::HiRes`, drop Perl Win32 dependency [163] o schannel: add an error message for client cert not found [165] o schannel: assume `CERT_CHAIN_REVOCATION_CHECK_CHAIN` [114] o schannel: drop fallbacks for 4 macros [121] @@ -163,6 +175,7 @@ This release includes the following bugfixes: o tidy-up: move literal to the right side of comparisons [65] o tidy-up: prefer `ifdef`/`ifndef` for single checks [64] o tls: CURLINFO_TLS_SSL_PTR testing [79] + o TODO: remove session export item [194] o tool_cb_wrt: stop alloc/free for every chunk windows console output [140] o tool_operate: avoid superfluous strdup'ing output [1] o tool_operate: use the correct config pointer [115] @@ -171,6 +184,7 @@ This release includes the following bugfixes: o tool_urlglob: polish, cleanups, improvements [141] o unit-tests: build the unitprotos.h from here [73] o unit2604: avoid `UNCONST()` [135] + o URL-SYNTAX.md: drop link to codepoints.net to pass linkcheck [190] o urlapi: allow more path characters "raw" when asked to URL encode [146] o urldata: reduce two long struct fields to unsigned short [174] o vquic-tls: fix SSL backend type for QUIC connections using gnutls [29] @@ -210,16 +224,17 @@ advice from friends like these: adamse on github, Ahmad Gani, Alice Lee Poetics, Ammar Faizi, Anthony Hu, Berthin Torres Callañaupa, Caolán McNamara, Cole Leavitt, d1r3ct0r, - Dan Fandrich, Daniel Böhmer, Daniel Stenberg, David Zhuang, devgs on github, - Dominik Tomecki, Eshan Kelkar, Harry Sintonen, IoannisGS on github, - Jeroen Ooms, Kai Pastor, Karthik Das, kkmuffme on github, - letshack9707 on hackerone, lf- on github, LoRd_MuldeR, Michał Petryka, - nevakrien on github, Oxan van Leeuwen, Paul Gilmartin, Petar Popovic, - Philippe Antoine, Pino Toscano, Qriist, Qriist on github, Ray Satiro, - renovate[bot], rm-rmonaghan on github, Roberto Hidalgo, Schrijvers Luc, + Dan Fandrich, Daniel Böhmer, Daniel Engberg, Daniel Stenberg, David Zhuang, + devgs on github, Dominik Tomecki, Eshan Kelkar, Google Big Sleep, + Harry Sintonen, IoannisGS on github, Jelle Raaijmakers, Jeroen Ooms, + Kai Pastor, Karthik Das, kkmuffme on github, letshack9707 on hackerone, + lf- on github, LoRd_MuldeR, Michał Petryka, nevakrien on github, + Oxan van Leeuwen, Paul Gilmartin, Petar Popovic, Philippe Antoine, + Pino Toscano, Qriist, Qriist on github, Ray Satiro, renovate[bot], + rm-rmonaghan on github, Roberto Hidalgo, Samuel Henrique, Schrijvers Luc, Sergio Durigan Junior, Stefan Eissing, Tal Regev, Todd Gamblin, Viktor Szakats, Waldemar Kornewald, yaoy6 on github, ウさん - (47 contributors) + (51 contributors) References to bug reports and discussions on issues: @@ -383,13 +398,27 @@ References to bug reports and discussions on issues: [160] = https://curl.se/bug/?i=18177 [161] = https://curl.se/bug/?i=18190 [162] = https://curl.se/bug/?i=18230 + [163] = https://curl.se/bug/?i=18287 + [164] = https://curl.se/bug/?i=18282 [165] = https://curl.se/bug/?i=18124 + [166] = https://curl.se/bug/?i=18271 [168] = https://curl.se/bug/?i=18170 [169] = https://curl.se/bug/?i=18171 + [172] = https://curl.se/bug/?i=18190 [173] = https://curl.se/bug/?i=18123 [174] = https://curl.se/bug/?i=18173 [175] = https://curl.se/bug/?i=18157 [176] = https://curl.se/bug/?i=17974 [178] = https://curl.se/bug/?i=18216 + [179] = https://curl.se/bug/?i=18272 + [180] = https://curl.se/bug/?i=18254 [183] = https://curl.se/bug/?i=18208 [184] = https://curl.se/bug/?i=18206 + [185] = https://curl.se/bug/?i=18266 + [186] = https://curl.se/bug/?i=18264 + [187] = https://curl.se/bug/?i=18246 + [189] = https://curl.se/bug/?i=18260 + [190] = https://curl.se/bug/?i=18259 + [193] = https://curl.se/bug/?i=17683 + [194] = https://curl.se/bug/?i=18243 + [196] = https://curl.se/bug/?i=18247 diff --git a/docs/examples/cacertinmem.c b/docs/examples/cacertinmem.c index 3a409dc5b..9b39667af 100644 --- a/docs/examples/cacertinmem.c +++ b/docs/examples/cacertinmem.c @@ -49,22 +49,22 @@ static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) /* replace the XXX with the actual CA certificates */ static const char mypem[] = "-----BEGIN CERTIFICATE-----\n" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "-----END CERTIFICATE-----\n" "-----BEGIN CERTIFICATE-----\n" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "-----END CERTIFICATE-----\n"; BIO *cbio = BIO_new_mem_buf(mypem, sizeof(mypem)); diff --git a/docs/examples/usercertinmem.c b/docs/examples/usercertinmem.c index 49722ca30..536b65b49 100644 --- a/docs/examples/usercertinmem.c +++ b/docs/examples/usercertinmem.c @@ -61,36 +61,36 @@ static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) const char *mypem = /* replace the XXX with the actual CA certificate */ - "-----BEGIN CERTIFICATE-----\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ + "-----BEGIN CERTIFICATE-----\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "-----END CERTIFICATE-----\n"; /* replace the XXX with the actual RSA key */ const char *mykey = - "-----BEGIN RSA PRIVATE KEY-----\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"\ + "-----BEGIN RSA PRIVATE KEY-----\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "-----END RSA PRIVATE KEY-----\n"; (void)curl; /* avoid warnings */ diff --git a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md index 81edea682..6dc81a086 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md @@ -83,15 +83,14 @@ int main(void) { CURL *ch; CURLcode rv; - char *mypem = /* example CA cert PEM - shortened */ + char *mypem = /* CA cert in PEM format, replace the XXXs */ "-----BEGIN CERTIFICATE-----\n" - "MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290\n" - "IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB\n" - "IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA\n" - "Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO\n" - "GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk\n" - "zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW\n" - "omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "-----END CERTIFICATE-----\n"; curl_global_init(CURL_GLOBAL_ALL); diff --git a/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md b/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md index ebf4c2ec3..75e1dc8ed 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md @@ -60,7 +60,9 @@ callbacks to add additional validation code for certificates, and even to change the actual URI of an HTTPS request. For OpenSSL, asynchronous certificate verification via *SSL_set_retry_verify* -is supported. (Added in 8.3.0) +is supported. When *SSL_set_retry_verify* is set, the transfer is paused. +When verification should continue, call curl_easy_pause(3) to unpause +the transfer. (Added in 8.3.0, Pausing added in 8.16.0) The CURLOPT_SSL_CTX_FUNCTION(3) callback allows the application to reach in and modify SSL details in the connection without libcurl itself knowing @@ -134,15 +136,14 @@ int main(void) { CURL *ch; CURLcode rv; - char *mypem = /* example CA cert PEM - shortened */ + char *mypem = /* CA cert in PEM format, replace the XXXs */ "-----BEGIN CERTIFICATE-----\n" - "MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290\n" - "IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB\n" - "IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA\n" - "Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO\n" - "GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk\n" - "zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW\n" - "omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "-----END CERTIFICATE-----\n"; curl_global_init(CURL_GLOBAL_ALL); diff --git a/lib/multi.c b/lib/multi.c index d38155cd9..1c09b14b6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1026,9 +1026,13 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, case MSTATE_CONNECTING: case MSTATE_TUNNELING: - result = mstate_connecting_pollset(data, ps); - if(!result) - result = Curl_conn_adjust_pollset(data, data->conn, ps); + if(!Curl_xfer_recv_is_paused(data)) { + result = mstate_connecting_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, data->conn, ps); + } + else + expect_sockets = FALSE; break; case MSTATE_PROTOCONNECT: @@ -2434,22 +2438,24 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ DEBUGASSERT(data->conn); - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); - if(connected && !result) { - if(!data->conn->bits.reuse && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); + if(!Curl_xfer_recv_is_paused(data)) { + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); + if(connected && !result) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); + } + rc = CURLM_CALL_MULTI_PERFORM; + multistate(data, MSTATE_PROTOCONNECT); + } + else if(result) { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, result, TRUE); + stream_error = TRUE; + break; } - rc = CURLM_CALL_MULTI_PERFORM; - multistate(data, MSTATE_PROTOCONNECT); - } - else if(result) { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - break; } break; diff --git a/lib/sendf.c b/lib/sendf.c index 6bd4b1bfb..43b30ecc7 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -293,9 +293,9 @@ static CURLcode cw_download_write(struct Curl_easy *data, } if((type & CLIENTWRITE_EOS) && !data->req.no_body && - (data->req.maxdownload > data->req.bytecount)) { + (data->req.size > data->req.bytecount)) { failf(data, "end of response with %" FMT_OFF_T " bytes missing", - data->req.maxdownload - data->req.bytecount); + data->req.size - data->req.bytecount); return CURLE_PARTIAL_FILE; } } diff --git a/lib/socks.c b/lib/socks.c index 4f9c98dba..8084489c9 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -529,6 +529,110 @@ CONNECT_REQ_INIT: return CURLPX_OK; /* Proxy was successful! */ } +static CURLproxycode socks5_init(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data, + const bool socks5_resolve_local, + const size_t hostname_len) +{ + struct connectdata *conn = cf->conn; + const unsigned char auth = data->set.socks5auth; + unsigned char *socksreq = sx->buffer; + + if(conn->bits.httpproxy) + CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %d", + sx->hostname, sx->remote_port); + + /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ + if(!socks5_resolve_local && hostname_len > 255) { + failf(data, "SOCKS5: the destination hostname is too long to be " + "resolved remotely by the proxy."); + return CURLPX_LONG_HOSTNAME; + } + + if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) + infof(data, "warning: unsupported value passed to " + "CURLOPT_SOCKS5_AUTH: %u", auth); + if(!(auth & CURLAUTH_BASIC)) + /* disable username/password auth */ + sx->proxy_user = NULL; + + if(!sx->outstanding) { + size_t idx = 0; + socksreq[idx++] = 5; /* version */ + idx++; /* number of authentication methods */ + socksreq[idx++] = 0; /* no authentication */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + if(auth & CURLAUTH_GSSAPI) + socksreq[idx++] = 1; /* GSS-API */ +#endif + if(sx->proxy_user) + socksreq[idx++] = 2; /* username/password */ + /* write the number of authentication methods */ + socksreq[1] = (unsigned char) (idx - 2); + + sx->outp = socksreq; + DEBUGASSERT(idx <= sizeof(sx->buffer)); + sx->outstanding = idx; + } + + return socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "initial SOCKS5 request"); +} + +static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data) +{ + /* Needs username and password */ + size_t proxy_user_len, proxy_password_len; + size_t len = 0; + unsigned char *socksreq = sx->buffer; + + if(sx->proxy_user && sx->proxy_password) { + proxy_user_len = strlen(sx->proxy_user); + proxy_password_len = strlen(sx->proxy_password); + } + else { + proxy_user_len = 0; + proxy_password_len = 0; + } + + /* username/password request looks like + * +----+------+----------+------+----------+ + * |VER | ULEN | UNAME | PLEN | PASSWD | + * +----+------+----------+------+----------+ + * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + * +----+------+----------+------+----------+ + */ + socksreq[len++] = 1; /* username/pw subnegotiation version */ + socksreq[len++] = (unsigned char) proxy_user_len; + if(sx->proxy_user && proxy_user_len) { + /* the length must fit in a single byte */ + if(proxy_user_len > 255) { + failf(data, "Excessive username length for proxy auth"); + return CURLPX_LONG_USER; + } + memcpy(socksreq + len, sx->proxy_user, proxy_user_len); + } + len += proxy_user_len; + socksreq[len++] = (unsigned char) proxy_password_len; + if(sx->proxy_password && proxy_password_len) { + /* the length must fit in a single byte */ + if(proxy_password_len > 255) { + failf(data, "Excessive password length for proxy auth"); + return CURLPX_LONG_PASSWD; + } + memcpy(socksreq + len, sx->proxy_password, proxy_password_len); + } + len += proxy_password_len; + sxstate(sx, cf, data, CONNECT_AUTH_SEND); + DEBUGASSERT(len <= sizeof(sx->buffer)); + sx->outstanding = len; + sx->outp = socksreq; + return CURLPX_OK; +} + /* * This function logs in to a SOCKS5 proxy and sends the specifics to the final * destination server. @@ -555,66 +659,20 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, */ struct connectdata *conn = cf->conn; unsigned char *socksreq = sx->buffer; - size_t idx; CURLcode result; CURLproxycode presult; bool socks5_resolve_local = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5); const size_t hostname_len = strlen(sx->hostname); size_t len = 0; - const unsigned char auth = data->set.socks5auth; bool allow_gssapi = FALSE; struct Curl_dns_entry *dns = NULL; switch(sx->state) { case CONNECT_SOCKS_INIT: - if(conn->bits.httpproxy) - CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %d", - sx->hostname, sx->remote_port); - - /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ - if(!socks5_resolve_local && hostname_len > 255) { - failf(data, "SOCKS5: the destination hostname is too long to be " - "resolved remotely by the proxy."); - return CURLPX_LONG_HOSTNAME; - } - - if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) - infof(data, "warning: unsupported value passed to " - "CURLOPT_SOCKS5_AUTH: %u", auth); - if(!(auth & CURLAUTH_BASIC)) - /* disable username/password auth */ - sx->proxy_user = NULL; -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(auth & CURLAUTH_GSSAPI) - allow_gssapi = TRUE; -#endif - - if(!sx->outstanding) { - idx = 0; - socksreq[idx++] = 5; /* version */ - idx++; /* number of authentication methods */ - socksreq[idx++] = 0; /* no authentication */ - if(allow_gssapi) - socksreq[idx++] = 1; /* GSS-API */ - if(sx->proxy_user) - socksreq[idx++] = 2; /* username/password */ - /* write the number of authentication methods */ - socksreq[1] = (unsigned char) (idx - 2); - - sx->outp = socksreq; - DEBUGASSERT(idx <= sizeof(sx->buffer)); - sx->outstanding = idx; - } - - presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, - "initial SOCKS5 request"); - if(CURLPX_OK != presult) + presult = socks5_init(cf, sx, data, socks5_resolve_local, hostname_len); + if(presult || sx->outstanding) return presult; - else if(sx->outstanding) { - /* remain in sending state */ - return CURLPX_OK; - } sxstate(sx, cf, data, CONNECT_SOCKS_READ); goto CONNECT_SOCKS_READ_INIT; case CONNECT_SOCKS_SEND: @@ -635,6 +693,10 @@ CONNECT_SOCKS_READ_INIT: case CONNECT_SOCKS_READ: presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, "initial SOCKS5 response"); +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + if(data->set.socks5auth & CURLAUTH_GSSAPI) + allow_gssapi = TRUE; +#endif if(CURLPX_OK != presult) return presult; else if(sx->outstanding) { @@ -690,52 +752,10 @@ CONNECT_SOCKS_READ_INIT: break; CONNECT_AUTH_INIT: - case CONNECT_AUTH_INIT: { - /* Needs username and password */ - size_t proxy_user_len, proxy_password_len; - if(sx->proxy_user && sx->proxy_password) { - proxy_user_len = strlen(sx->proxy_user); - proxy_password_len = strlen(sx->proxy_password); - } - else { - proxy_user_len = 0; - proxy_password_len = 0; - } - - /* username/password request looks like - * +----+------+----------+------+----------+ - * |VER | ULEN | UNAME | PLEN | PASSWD | - * +----+------+----------+------+----------+ - * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | - * +----+------+----------+------+----------+ - */ - len = 0; - socksreq[len++] = 1; /* username/pw subnegotiation version */ - socksreq[len++] = (unsigned char) proxy_user_len; - if(sx->proxy_user && proxy_user_len) { - /* the length must fit in a single byte */ - if(proxy_user_len > 255) { - failf(data, "Excessive username length for proxy auth"); - return CURLPX_LONG_USER; - } - memcpy(socksreq + len, sx->proxy_user, proxy_user_len); - } - len += proxy_user_len; - socksreq[len++] = (unsigned char) proxy_password_len; - if(sx->proxy_password && proxy_password_len) { - /* the length must fit in a single byte */ - if(proxy_password_len > 255) { - failf(data, "Excessive password length for proxy auth"); - return CURLPX_LONG_PASSWD; - } - memcpy(socksreq + len, sx->proxy_password, proxy_password_len); - } - len += proxy_password_len; - sxstate(sx, cf, data, CONNECT_AUTH_SEND); - DEBUGASSERT(len <= sizeof(sx->buffer)); - sx->outstanding = len; - sx->outp = socksreq; - } + case CONNECT_AUTH_INIT: + presult = socks5_auth_init(cf, sx, data); + if(presult) + return presult; FALLTHROUGH(); case CONNECT_AUTH_SEND: presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH, diff --git a/lib/vssh/wolfssh.c b/lib/vssh/wolfssh.c index 556eda0cb..7cd0402a9 100644 --- a/lib/vssh/wolfssh.c +++ b/lib/vssh/wolfssh.c @@ -463,6 +463,150 @@ error: return CURLE_FAILED_INIT; } +static CURLcode wssh_sftp_upload_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sftp_scp, + bool *block) +{ + word32 flags; + WS_SFTP_FILEATRB createattrs; + struct connectdata *conn = data->conn; + int rc; + if(data->state.resume_from) { + WS_SFTP_FILEATRB attrs; + if(data->state.resume_from < 0) { + rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, + &attrs); + if(rc != WS_SUCCESS) + return CURLE_SSH; + + if(rc) { + data->state.resume_from = 0; + } + else { + curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0]; + if(size < 0) { + failf(data, "Bad file size (%" FMT_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + data->state.resume_from = size; + } + } + } + + if(data->set.remote_append) + /* Try to open for append, but create if nonexisting */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND; + else if(data->state.resume_from > 0) + /* If we have restart position then open for append */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND; + else + /* Clear file before writing (normal behavior) */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC; + + memset(&createattrs, 0, sizeof(createattrs)); + createattrs.per = (word32)data->set.new_file_perms; + sshc->handleSz = sizeof(sshc->handle); + rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, + flags, &createattrs, + sshc->handle, &sshc->handleSz); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh SFTP open succeeded"); + } + else { + failf(data, "wolfssh SFTP upload open failed: %d", rc); + return CURLE_SSH; + } + wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT); + + /* If we have a restart point then we need to seek to the correct + position. */ + if(data->state.resume_from > 0) { + /* Let's read off the proper amount of bytes from the input. */ + int seekerr = CURL_SEEKFUNC_OK; + if(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); + } + + 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); + + size_t actuallyread; + Curl_set_in_callback(data, TRUE); + actuallyread = data->state.fread_func(scratch, 1, + readthisamountnow, + data->state.in); + Curl_set_in_callback(data, FALSE); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed < data->state.resume_from); + } + + /* now, decrease the size of the read */ + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + + sshc->offset += data->state.resume_from; + } + if(data->state.infilesize > 0) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + /* upload data */ + Curl_xfer_setup_send(data, FIRSTSOCKET); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + data->conn->recv_idx = FIRSTSOCKET; + + /* store this original bitmask setup to use later on if we cannot + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* since we do not really wait for anything at this point, we want the state + machine to move on as soon as possible */ + Curl_multi_mark_dirty(data); + + wssh_state(data, sshc, SSH_STOP); + + return CURLE_OK; +} + /* * wssh_statemach_act() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points @@ -597,148 +741,10 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT); } break; - case SSH_SFTP_UPLOAD_INIT: { - word32 flags; - WS_SFTP_FILEATRB createattrs; - if(data->state.resume_from) { - WS_SFTP_FILEATRB attrs; - if(data->state.resume_from < 0) { - rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, - &attrs); - if(rc != WS_SUCCESS) - break; - - if(rc) { - data->state.resume_from = 0; - } - else { - curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0]; - if(size < 0) { - failf(data, "Bad file size (%" FMT_OFF_T ")", size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - data->state.resume_from = size; - } - } - } - - if(data->set.remote_append) - /* Try to open for append, but create if nonexisting */ - flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND; - else if(data->state.resume_from > 0) - /* If we have restart position then open for append */ - flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND; - else - /* Clear file before writing (normal behavior) */ - flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC; - - memset(&createattrs, 0, sizeof(createattrs)); - createattrs.per = (word32)data->set.new_file_perms; - sshc->handleSz = sizeof(sshc->handle); - rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, - flags, &createattrs, - sshc->handle, &sshc->handleSz); - if(rc == WS_FATAL_ERROR) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(rc == WS_SUCCESS) { - infof(data, "wolfssh SFTP open succeeded"); - } - else { - failf(data, "wolfssh SFTP upload open failed: %d", rc); - return CURLE_SSH; - } - wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT); - - /* If we have a restart point then we need to seek to the correct - position. */ - if(data->state.resume_from > 0) { - /* Let's read off the proper amount of bytes from the input. */ - int seekerr = CURL_SEEKFUNC_OK; - if(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); - } - - 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); - - size_t actuallyread; - Curl_set_in_callback(data, TRUE); - actuallyread = data->state.fread_func(scratch, 1, - readthisamountnow, - data->state.in); - Curl_set_in_callback(data, FALSE); - - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); - return CURLE_FTP_COULDNT_USE_REST; - } - } while(passed < data->state.resume_from); - } - - /* now, decrease the size of the read */ - if(data->state.infilesize > 0) { - data->state.infilesize -= data->state.resume_from; - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - - sshc->offset += data->state.resume_from; - } - if(data->state.infilesize > 0) { - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - /* upload data */ - Curl_xfer_setup_send(data, FIRSTSOCKET); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - data->conn->recv_idx = FIRSTSOCKET; - - if(result) { - wssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - /* store this original bitmask setup to use later on if we cannot - figure out a "real" bitmask */ - sshc->orig_waitfor = data->req.keepon; - - /* since we do not really wait for anything at this point, we want the - state machine to move on as soon as possible */ - Curl_multi_mark_dirty(data); - - wssh_state(data, sshc, SSH_STOP); - } + case SSH_SFTP_UPLOAD_INIT: + result = wssh_sftp_upload_init(data, sshc, sftp_scp, block); break; - } + case SSH_SFTP_DOWNLOAD_INIT: sshc->handleSz = sizeof(sshc->handle); rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 06ae10232..a820a98b8 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -107,19 +107,6 @@ struct mbed_ssl_backend_data { #define mbedtls_strerror(a,b,c) b[0] = 0 #endif -/* PSA can be used independently of TLS 1.3 */ -#if defined(MBEDTLS_USE_PSA_CRYPTO) && MBEDTLS_VERSION_NUMBER >= 0x03060000 -#define HAS_PSA_SUPPORT -#endif - -#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && MBEDTLS_VERSION_NUMBER >= 0x03060000 -#define HAS_TLS13_SUPPORT -#endif - -#if defined(HAS_TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS) -#define HAS_SESSION_TICKETS -#endif - #ifdef HAS_THREADING_SUPPORT static mbedtls_entropy_context ts_entropy; @@ -260,7 +247,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, mbedtls_ssl_protocol_version ver_min = MBEDTLS_SSL_VERSION_TLS1_2; mbedtls_ssl_protocol_version ver_max = -#ifdef HAS_TLS13_SUPPORT +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_VERSION_TLS1_3 #else MBEDTLS_SSL_VERSION_TLS1_2 @@ -276,7 +263,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, ver_min = MBEDTLS_SSL_VERSION_TLS1_2; break; case CURL_SSLVERSION_TLSv1_3: -#ifdef HAS_TLS13_SUPPORT +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 ver_min = MBEDTLS_SSL_VERSION_TLS1_3; break; #endif @@ -290,7 +277,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, case CURL_SSLVERSION_MAX_DEFAULT: case CURL_SSLVERSION_MAX_NONE: case CURL_SSLVERSION_MAX_TLSv1_3: -#ifdef HAS_TLS13_SUPPORT +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 ver_max = MBEDTLS_SSL_VERSION_TLS1_3; break; #endif @@ -363,7 +350,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, if(!selected) return CURLE_OUT_OF_MEMORY; -#ifndef HAS_TLS13_SUPPORT +#ifndef MBEDTLS_SSL_PROTO_TLS1_3 (void)ciphers13, (void)j; #else if(!ciphers13) { @@ -411,7 +398,7 @@ add_ciphers: selected[count++] = id; } -#ifdef HAS_TLS13_SUPPORT +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 if(ciphers == ciphers13 && ciphers12) { ciphers = ciphers12; goto add_ciphers; @@ -760,7 +747,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } -#if defined(HAS_SESSION_TICKETS) && MBEDTLS_VERSION_NUMBER >= 0x03060100 +#ifdef MBEDTLS_SSL_SESSION_TICKETS /* New in mbedTLS 3.6.1, need to enable, default is now disabled */ mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config, MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); @@ -799,7 +786,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_bio_cf_read, NULL /* rev_timeout() */); -#ifndef HAS_TLS13_SUPPORT +#ifndef MBEDTLS_SSL_PROTO_TLS1_3 if(conn_config->cipher_list) { CURLcode result = mbed_set_selected_ciphers(data, backend, conn_config->cipher_list, @@ -1149,7 +1136,7 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", len, -nwritten); result = ((nwritten == MBEDTLS_ERR_SSL_WANT_WRITE) -#ifdef HAS_TLS13_SUPPORT +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 || (nwritten == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) #endif ) ? CURLE_AGAIN : CURLE_SEND_ERROR; @@ -1219,7 +1206,7 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, * WANT_READ, but has not encountered an EAGAIN. */ if(ret == MBEDTLS_ERR_SSL_WANT_READ) ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf)); -#ifdef HAS_TLS13_SUPPORT +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 if(ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) continue; #endif @@ -1301,7 +1288,7 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", buffersize, -nread); switch(nread) { -#ifdef HAS_SESSION_TICKETS +#ifdef MBEDTLS_SSL_SESSION_TICKETS case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: mbed_new_session(cf, data); FALLTHROUGH(); @@ -1431,7 +1418,7 @@ static int mbedtls_init(void) #ifdef HAS_THREADING_SUPPORT entropy_init_mutex(&ts_entropy); #endif -#ifdef HAS_PSA_SUPPORT +#ifdef MBEDTLS_USE_PSA_CRYPTO /* requires mbedTLS 3.6.0+ */ { int ret; #ifdef HAS_THREADING_SUPPORT @@ -1444,7 +1431,7 @@ static int mbedtls_init(void) if(ret != PSA_SUCCESS) return 0; } -#endif /* HAS_PSA_SUPPORT */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ return 1; } @@ -1498,7 +1485,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { SSLSUPP_CERTINFO | SSLSUPP_PINNEDPUBKEY | SSLSUPP_SSL_CTX | -#ifdef HAS_TLS13_SUPPORT +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 /* requires mbedTLS 3.6.0+ */ SSLSUPP_TLS13_CIPHERSUITES | #endif SSLSUPP_HTTPS_PROXY | diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index dc4a6d122..e25c57304 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -61,6 +61,7 @@ #include "../vauth/vauth.h" #include "keylog.h" #include "hostcheck.h" +#include "../transfer.h" #include "../multiif.h" #include "../curlx/strparse.h" #include "../strdup.h" @@ -850,13 +851,19 @@ static void ossl_keylog_callback(const SSL *ssl, const char *line) static void ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) { - const SSL_SESSION *session = SSL_get_session(ssl); + const SSL_SESSION *session; unsigned char client_random[SSL3_RANDOM_SIZE]; unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; int master_key_length = 0; - if(!session || *keylog_done) + ERR_set_mark(); + + session = SSL_get_session(ssl); + + if(!session || *keylog_done) { + ERR_pop_to_mark(); return; + } #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* ssl->s3 is not checked in OpenSSL 1.1.0-pre6, but let's assume that @@ -872,6 +879,8 @@ ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) } #endif + ERR_pop_to_mark(); + /* The handshake has not progressed sufficiently yet, or this is a TLS 1.3 * session (when curl was built with older OpenSSL headers and running with * newer OpenSSL runtime libraries). */ @@ -3327,10 +3336,8 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, continue; x509 = d2i_X509(NULL, &encoded_cert, (long)pContext->cbCertEncoded); - if(!x509) { - ERR_clear_error(); + if(!x509) continue; - } /* Try to import the certificate. This may fail for legitimate reasons such as duplicate certificate, which is allowed by MS but @@ -3661,6 +3668,8 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, !ssl_config->primary.CRLfile && !ssl_config->native_ca_store; + ERR_set_mark(); + cached_store = ossl_get_cached_x509_store(cf, data); if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) { SSL_CTX_set_cert_store(ssl_ctx, cached_store); @@ -3674,6 +3683,8 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, } } + ERR_pop_to_mark(); + return result; } #else /* HAVE_SSL_X509_STORE_SHARE */ @@ -3681,9 +3692,17 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, SSL_CTX *ssl_ctx) { - X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); + CURLcode result; + X509_STORE *store; + + ERR_set_mark(); + + store = SSL_CTX_get_cert_store(ssl_ctx); + result = ossl_populate_x509_store(cf, data, store); - return ossl_populate_x509_store(cf, data, store); + ERR_pop_to_mark(); + + return result; } #endif /* HAVE_SSL_X509_STORE_SHARE */ @@ -4584,7 +4603,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, #ifdef SSL_ERROR_WANT_RETRY_VERIFY if(SSL_ERROR_WANT_RETRY_VERIFY == detail) { CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify"); - connssl->io_need = CURL_SSL_IO_NEED_RECV; + Curl_xfer_pause_recv(data, TRUE); return CURLE_AGAIN; } #endif diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 1c0258e17..950cb2544 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -108,7 +108,7 @@ test718 test719 test720 test721 test722 test723 test724 test725 test726 \ test727 test728 test729 test730 test731 test732 test733 test734 test735 \ test736 test737 test738 test739 test740 test741 test742 test743 test744 \ test745 test746 test747 test748 test749 test750 test751 test752 test753 \ -test754 test755 test756 test757 \ +test754 test755 test756 test757 test758 \ test780 test781 test782 test783 test784 test785 test786 test787 test788 \ test789 test790 test791 test792 test793 test794 test796 test797 \ \ @@ -256,8 +256,8 @@ test2100 test2101 test2102 test2103 test2104 \ \ test2200 test2201 test2202 test2203 test2204 test2205 \ \ -test2300 test2301 test2302 test2303 test2304 test2306 \ -test2308 test2309 \ +test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ +test2309 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 \ \ diff --git a/tests/data/test2307 b/tests/data/test2307 new file mode 100644 index 000000000..cbb581a4a --- /dev/null +++ b/tests/data/test2307 @@ -0,0 +1,60 @@ +<testcase> +<info> +<keywords> +FTP +EPSV +RETR +Range +</keywords> +</info> +# Server-side +<reply> +<data> +0123456789abcdef +0123456789abcdef +0123456789abcdef +0123456789abcdef +</data> +<datacheck nonewline="yes"> +0123456789abcdef +0123456789abcdef +0123456789abcdef +012345678 +</datacheck> +<size> +64 +</size> +</reply> + +# Client-side +<client> +<server> +ftp +</server> +<name> +FTP retrieve a byte-range with end larger than file +</name> +<command> +-r 4-1000 ftp://%HOSTIP:%FTPPORT/%TESTNUMBER +</command> +</client> + +# Verify data after the test has been "shot" +<verify> +<strip> +QUIT +</strip> +<protocol crlf="yes"> +USER anonymous +PASS ftp@example.com +PWD +EPSV +TYPE I +SIZE %TESTNUMBER +REST 4 +RETR %TESTNUMBER +ABOR +QUIT +</protocol> +</verify> +</testcase> diff --git a/tests/data/test758 b/tests/data/test758 new file mode 100644 index 000000000..a807ae9c8 --- /dev/null +++ b/tests/data/test758 @@ -0,0 +1,53 @@ +<testcase> +<info> +<keywords> +multi +HTTPS +</keywords> +</info> + +# Server-side +<reply> +<data nocheck="yes"> +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6007 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- +%repeat[1000 x foobar]% +</data> +</reply> + +<features> +OpenSSL +</features> + +# Client-side +<client> +<server> +https +</server> +<tool> +lib%TESTNUMBER +</tool> +<name> +multi_socket interface transfer with callbacks returning error +</name> +<command> +https://localhost:%HTTPSPORT/file%TESTNUMBER +</command> +</client> + +# Verify data after the test has been "shot" +<verify> +<protocol> +</protocol> +</verify> +</testcase> diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index 927d4c66a..71d6774fd 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -51,6 +51,7 @@ BEGIN { use IPC::Open2; use Digest::MD5; use File::Basename; +use Time::HiRes; use directories; @@ -484,7 +485,7 @@ sub sendcontrol { for(@a) { sockfilt $_; - portable_sleep($ctrldelay); + Time::HiRes::sleep($ctrldelay); } } my $log; @@ -521,7 +522,7 @@ sub senddata { # pause between each byte for (split(//,$l)) { sockfiltsecondary $_; - portable_sleep($datadelay); + Time::HiRes::sleep($datadelay); } } } @@ -3292,7 +3293,7 @@ while(1) { logmsg("Sleep for $delay seconds\n"); my $twentieths = $delay * 20; while($twentieths--) { - portable_sleep(0.05) unless($got_exit_signal); + Time::HiRes::sleep(0.05) unless($got_exit_signal); } } diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 5e1462108..6f51040c1 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -71,7 +71,7 @@ TESTS_C = \ lib661.c lib666.c lib667.c lib668.c \ lib670.c lib674.c lib676.c lib677.c lib678.c \ lib694.c lib695.c \ - lib751.c lib753.c \ + lib751.c lib753.c lib758.c \ lib757.c \ lib1156.c \ lib1301.c lib1308.c \ diff --git a/tests/libtest/lib758.c b/tests/libtest/lib758.c new file mode 100644 index 000000000..dfcbaebb3 --- /dev/null +++ b/tests/libtest/lib758.c @@ -0,0 +1,515 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* + * The purpose of this test is to make sure that if CURLMOPT_SOCKETFUNCTION or + * CURLMOPT_TIMERFUNCTION returns error, the associated transfer should be + * aborted correctly. + */ + +#include "first.h" + +#include "testtrace.h" +#include "memdebug.h" + +#ifdef USE_OPENSSL + +#include <openssl/x509.h> +#include <openssl/ssl.h> + +#if ((OPENSSL_VERSION_NUMBER >= 0x30000000L) && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL)) +#define T578_ENABLED +#endif +#endif + +#ifdef T578_ENABLED + +static struct t758_ctx { + int socket_calls; + int max_socket_calls; + int timer_calls; + int max_timer_calls; + int fake_async_cert_verification_pending; + int fake_async_cert_verification_finished; + int number_of_cert_verify_callbacks; + char buf[1024]; +} t758_ctx; + +static const char *t758_tag(void) +{ + curl_msnprintf(t758_ctx.buf, sizeof(t758_ctx.buf), + "[T758-%d-%d] [%d/%d]", + t758_ctx.max_socket_calls, t758_ctx.max_timer_calls, + t758_ctx.socket_calls, t758_ctx.timer_calls); + return t758_ctx.buf; +} + +static void t758_msg(const char *msg) +{ + curl_mfprintf(stderr, "%s %s\n", t758_tag(), msg); +} + + +struct t758_Sockets { + curl_socket_t *sockets; + int count; /* number of sockets actually stored in array */ + int max_count; /* max number of sockets that fit in allocated array */ +}; + +struct t758_ReadWriteSockets { + struct t758_Sockets read, write; +}; + +/** + * Remove a file descriptor from a sockets array. + */ +static void t758_removeFd(struct t758_Sockets *sockets, curl_socket_t fd, + int mention) +{ + int i; + + if(mention) + curl_mfprintf(stderr, "%s remove socket fd %" FMT_SOCKET_T "\n", + t758_tag(), fd); + + for(i = 0; i < sockets->count; ++i) { + if(sockets->sockets[i] == fd) { + if(i < sockets->count - 1) + memmove(&sockets->sockets[i], &sockets->sockets[i + 1], + sizeof(curl_socket_t) * (sockets->count - (i + 1))); + --sockets->count; + } + } +} + +/** + * Add a file descriptor to a sockets array. + * Return 0 on success, 1 on error. + */ +static int t758_addFd(struct t758_Sockets *sockets, curl_socket_t fd, + const char *what) +{ + /** + * To ensure we only have each file descriptor once, we remove it then add + * it again. + */ + curl_mfprintf(stderr, "%s add socket fd %" FMT_SOCKET_T " for %s\n", + t758_tag(), fd, what); + t758_removeFd(sockets, fd, 0); + /* + * Allocate array storage when required. + */ + if(!sockets->sockets) { + sockets->sockets = malloc(sizeof(curl_socket_t) * 20U); + if(!sockets->sockets) + return 1; + sockets->max_count = 20; + } + else if(sockets->count + 1 > sockets->max_count) { + curl_socket_t *ptr = realloc(sockets->sockets, sizeof(curl_socket_t) * + (sockets->max_count + 20)); + if(!ptr) + /* cleanup in test_cleanup */ + return 1; + sockets->sockets = ptr; + sockets->max_count += 20; + } + /* + * Add file descriptor to array. + */ + sockets->sockets[sockets->count] = fd; + ++sockets->count; + return 0; +} + +/** + * Callback invoked by curl to poll reading / writing of a socket. + */ +static int t758_curlSocketCallback(CURL *easy, curl_socket_t s, int action, + void *userp, void *socketp) +{ + struct t758_ReadWriteSockets *sockets = userp; + + (void)easy; + (void)socketp; + + t758_ctx.socket_calls++; + t758_msg("-> CURLMOPT_SOCKETFUNCTION"); + if(t758_ctx.socket_calls == t758_ctx.max_socket_calls) { + t758_msg("<- CURLMOPT_SOCKETFUNCTION returns error"); + return -1; + } + + if(action == CURL_POLL_IN || action == CURL_POLL_INOUT) + if(t758_addFd(&sockets->read, s, "read")) + return -1; /* bail out */ + + if(action == CURL_POLL_OUT || action == CURL_POLL_INOUT) + if(t758_addFd(&sockets->write, s, "write")) + return -1; + + if(action == CURL_POLL_REMOVE) { + t758_removeFd(&sockets->read, s, 1); + t758_removeFd(&sockets->write, s, 0); + } + + return 0; +} + +/** + * Callback invoked by curl to set a timeout. + */ +static int t758_curlTimerCallback(CURLM *multi, long timeout_ms, void *userp) +{ + struct curltime *timeout = userp; + + (void)multi; /* unused */ + t758_ctx.timer_calls++; + t758_msg("-> CURLMOPT_TIMERFUNCTION"); + if(t758_ctx.timer_calls == t758_ctx.max_timer_calls) { + t758_msg("<- CURLMOPT_TIMERFUNCTION returns error"); + return -1; + } + if(timeout_ms != -1) { + *timeout = curlx_now(); + timeout->tv_usec += (int)timeout_ms * 1000; + } + else { + timeout->tv_sec = -1; + } + return 0; +} + +static int t758_cert_verify_callback(X509_STORE_CTX *ctx, void *arg) +{ + SSL * ssl; + (void)arg; /* unused */ + ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + t758_ctx.number_of_cert_verify_callbacks++; + if(!t758_ctx.fake_async_cert_verification_pending) { + t758_ctx.fake_async_cert_verification_pending = 1; + t758_msg(" initial t758_cert_verify_callback"); + return SSL_set_retry_verify(ssl); + } + else if(t758_ctx.fake_async_cert_verification_finished) { + t758_msg(" final t758_cert_verify_callback"); + return 1; /* success */ + } + else { + t758_msg(" pending t758_cert_verify_callback"); + return SSL_set_retry_verify(ssl); + } +} + +static CURLcode +t758_set_ssl_ctx_callback(CURL *curl, void *ssl_ctx, void *clientp) +{ + SSL_CTX *ctx = (SSL_CTX *) ssl_ctx; + (void)curl; /* unused */ + SSL_CTX_set_cert_verify_callback(ctx, t758_cert_verify_callback, clientp); + return CURLE_OK; +} + +/** + * Check for curl completion. + */ +static int t758_checkForCompletion(CURLM *curl, int *success) +{ + int result = 0; + *success = 0; + while(1) { + int numMessages; + CURLMsg *message = curl_multi_info_read(curl, &numMessages); + if(!message) + break; + if(message->msg == CURLMSG_DONE) { + result = 1; + if(message->data.result == CURLE_OK) + *success = 1; + else + *success = 0; + } + else { + curl_mfprintf(stderr, "%s got an unexpected message from curl: %i\n", + t758_tag(), message->msg); + result = 1; + *success = 0; + } + } + return result; +} + +static ssize_t t758_getMicroSecondTimeout(struct curltime *timeout) +{ + struct curltime now; + ssize_t result; + now = curlx_now(); + result = (ssize_t)((timeout->tv_sec - now.tv_sec) * 1000000 + + timeout->tv_usec - now.tv_usec); + if(result < 0) + result = 0; + + return result; +} + +/** + * Update a fd_set with all of the sockets in use. + */ +static void t758_updateFdSet(struct t758_Sockets *sockets, fd_set* fdset, + curl_socket_t *maxFd) +{ + int i; + for(i = 0; i < sockets->count; ++i) { +#ifdef __DJGPP__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warith-conversion" +#endif + FD_SET(sockets->sockets[i], fdset); +#ifdef __DJGPP__ +#pragma GCC diagnostic pop +#endif + if(*maxFd < sockets->sockets[i] + 1) { + *maxFd = sockets->sockets[i] + 1; + } + } +} + +static CURLMcode t758_saction(CURLM *curl, curl_socket_t s, + int evBitmask, const char *info) +{ + int numhandles = 0; + CURLMcode result = curl_multi_socket_action(curl, s, evBitmask, &numhandles); + if(result != CURLM_OK) { + curl_mfprintf(stderr, "%s Curl error on %s (%i) %s\n", + t758_tag(), info, result, curl_multi_strerror(result)); + } + return result; +} + +/** + * Invoke curl when a file descriptor is set. + */ +static CURLMcode t758_checkFdSet(CURLM *curl, struct t758_Sockets *sockets, + fd_set *fdset, int evBitmask, + const char *name) +{ + int i; + CURLMcode result = CURLM_OK; + for(i = 0; i < sockets->count; ++i) { + if(FD_ISSET(sockets->sockets[i], fdset)) { + result = t758_saction(curl, sockets->sockets[i], evBitmask, name); + if(result) + break; + } + } + return result; +} + +static CURLcode t758_one(const char *URL, int timer_fail_at, + int socket_fail_at) +{ + CURLcode res = CURLE_OK; + CURL *curl = NULL; CURLM *m = NULL; + struct t758_ReadWriteSockets sockets = {{NULL, 0, 0}, {NULL, 0, 0}}; + int success = 0; + struct curltime timeout = {0}; + timeout.tv_sec = (time_t)-1; + + /* set the limits */ + memset(&t758_ctx, 0, sizeof(t758_ctx)); + t758_ctx.max_timer_calls = timer_fail_at; + t758_ctx.max_socket_calls = socket_fail_at; + + t758_msg("start"); + start_test_timing(); + + if(curl_global_sslset(CURLSSLBACKEND_OPENSSL, NULL, NULL) != CURLSSLSET_OK) { + t758_msg("could not set OpenSSL as backend"); + res = CURLE_FAILED_INIT; + return res; + } + + res_global_init(CURL_GLOBAL_ALL); + if(res != CURLE_OK) + return res; + + curl_global_trace("all"); + + + easy_init(curl); + debug_config.nohex = TRUE; + debug_config.tracetime = TRUE; + test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + /* specify target */ + easy_setopt(curl, CURLOPT_URL, URL); + + /* go verbose */ + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, t758_set_ssl_ctx_callback); + + multi_init(m); + + multi_setopt(m, CURLMOPT_SOCKETFUNCTION, t758_curlSocketCallback); + multi_setopt(m, CURLMOPT_SOCKETDATA, &sockets); + + multi_setopt(m, CURLMOPT_TIMERFUNCTION, t758_curlTimerCallback); + multi_setopt(m, CURLMOPT_TIMERDATA, &timeout); + + multi_add_handle(m, curl); + + if(t758_saction(m, CURL_SOCKET_TIMEOUT, 0, "timeout")) { + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + while(!t758_checkForCompletion(m, &success)) { + fd_set readSet, writeSet; + curl_socket_t maxFd = 0; + struct timeval tv = {0}; + tv.tv_sec = 10; + + if(t758_ctx.fake_async_cert_verification_pending && + !t758_ctx.fake_async_cert_verification_finished) { + if(sockets.read.count || sockets.write.count) { + t758_msg("during verification there should be no sockets scheduled"); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + if(t758_ctx.number_of_cert_verify_callbacks != 1) { + t758_msg("expecting exactly one cert verify callback here"); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + t758_ctx.fake_async_cert_verification_finished = 1; + if(t758_saction(m, CURL_SOCKET_TIMEOUT, 0, "timeout")) { + t758_msg("spurious retry cert action"); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + curl_easy_pause(curl, CURLPAUSE_CONT); + if(t758_saction(m, CURL_SOCKET_TIMEOUT, 0, "timeout")) { + t758_msg("unblocking transfer after cert verification finished"); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + if(t758_ctx.number_of_cert_verify_callbacks != 2) { + t758_msg("this should have triggered the callback again, right?"); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + t758_msg("TEST: all fine?"); + } + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + t758_updateFdSet(&sockets.read, &readSet, &maxFd); + t758_updateFdSet(&sockets.write, &writeSet, &maxFd); + + if(timeout.tv_sec != (time_t)-1) { + int usTimeout = curlx_sztosi(t758_getMicroSecondTimeout(&timeout)); + tv.tv_sec = usTimeout / 1000000; + tv.tv_usec = usTimeout % 1000000; + } + else if(maxFd <= 0) { + tv.tv_sec = 0; + tv.tv_usec = 100000; + } + + select_test((int)maxFd, &readSet, &writeSet, NULL, &tv); + + /* Check the sockets for reading / writing */ + if(t758_checkFdSet(m, &sockets.read, &readSet, CURL_CSELECT_IN, + "read")) { + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + if(t758_checkFdSet(m, &sockets.write, &writeSet, CURL_CSELECT_OUT, + "write")) { + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + if(timeout.tv_sec != (time_t)-1 && + t758_getMicroSecondTimeout(&timeout) == 0) { + /* Curl's timer has elapsed. */ + if(t758_saction(m, CURL_SOCKET_TIMEOUT, 0, "timeout")) { + res = TEST_ERR_BAD_TIMEOUT; + goto test_cleanup; + } + } + + abort_on_test_timeout(); + } + if(success && t758_ctx.number_of_cert_verify_callbacks != 2) { + t758_msg("unexpected invocations of cert verify callback"); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + if(!success) { + t758_msg("Error getting file."); + res = TEST_ERR_MAJOR_BAD; + } + +test_cleanup: + + /* proper cleanup sequence */ + t758_msg("cleanup"); + curl_multi_remove_handle(m, curl); + curl_easy_cleanup(curl); + curl_multi_cleanup(m); + curl_global_cleanup(); + + /* free local memory */ + free(sockets.read.sockets); + free(sockets.write.sockets); + t758_msg("done"); + + return res; +} + +static CURLcode test_lib758(const char *URL) +{ + CURLcode rc; + /* rerun the same transfer multiple times and make it fail in different + callback calls */ + rc = t758_one(URL, 0, 0); /* no callback fails */ + if(rc) + curl_mfprintf(stderr, "%s FAILED: %d\n", t758_tag(), rc); + + return rc; +} + +#else /* T578_ENABLED */ +static CURLcode test_lib758(const char *URL) +{ + (void)URL; + return CURLE_OK; +} +#endif diff --git a/tests/processhelp.pm b/tests/processhelp.pm index 571ed5b34..43513aae0 100644 --- a/tests/processhelp.pm +++ b/tests/processhelp.pm @@ -27,11 +27,12 @@ package processhelp; use strict; use warnings; +use Time::HiRes; + BEGIN { use base qw(Exporter); our @EXPORT = qw( - portable_sleep pidfromfile pidexists pidwait @@ -42,17 +43,6 @@ BEGIN { set_advisor_read_lock clear_advisor_read_lock ); - - # portable sleeping needs Time::HiRes - eval { - no warnings "all"; - require Time::HiRes; - }; - # portable sleeping falls back to native Sleep on Windows - eval { - no warnings "all"; - require Win32; - } } use serverhelp qw( @@ -65,26 +55,9 @@ use pathhelp qw( os_is_win ); -####################################################################### -# portable_sleep uses Time::HiRes::sleep if available and falls back -# to the classic approach of using select(undef, undef, undef, ...). -# even though that one is not portable due to being implemented using -# select on Windows: https://perldoc.perl.org/perlport.html#select -# Therefore it uses Win32::Sleep on Windows systems instead. -# -sub portable_sleep { - my ($seconds) = @_; - - if($Time::HiRes::VERSION) { - Time::HiRes::sleep($seconds); - } - elsif(os_is_win()) { - Win32::Sleep($seconds*1000); - } - else { - select(undef, undef, undef, $seconds); - } -} +use globalconfig qw( + $dev_null + ); ####################################################################### # pidfromfile returns the pid stored in the given pidfile. The value @@ -143,7 +116,7 @@ sub pidexists { if($^O ne 'MSWin32') { my $filter = "PID eq $pid"; # https://ss64.com/nt/tasklist.html - my $result = `tasklist -fi \"$filter\" 2>nul`; + my $result = `tasklist -fi \"$filter\" 2>$dev_null`; if(index($result, "$pid") != -1) { return -$pid; } @@ -173,7 +146,7 @@ sub pidterm { $pid -= 4194304; if($^O ne 'MSWin32') { # https://ss64.com/nt/taskkill.html - my $cmd = "taskkill -f -t -pid $pid >nul 2>&1"; + my $cmd = "taskkill -f -t -pid $pid >$dev_null 2>&1"; print "Executing: '$cmd'\n"; system($cmd); return; @@ -198,7 +171,7 @@ sub pidkill { $pid -= 4194304; if($^O ne 'MSWin32') { # https://ss64.com/nt/taskkill.html - my $cmd = "taskkill -f -t -pid $pid >nul 2>&1"; + my $cmd = "taskkill -f -t -pid $pid >$dev_null 2>&1"; print "Executing: '$cmd'\n"; system($cmd); return; @@ -234,7 +207,7 @@ sub pidwait { last; } } - portable_sleep(0.2); + Time::HiRes::sleep(0.2); } return $pid; } @@ -342,7 +315,7 @@ sub killpid { last if(not scalar(@signalled)); # give any zombies of us a chance to move on to the afterlife pidwait(0, &WNOHANG); - portable_sleep(0.05); + Time::HiRes::sleep(0.05); } } diff --git a/tests/runner.pm b/tests/runner.pm index a208eba3a..18f3c5098 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -39,6 +39,7 @@ use warnings; use 5.006; use File::Basename; +use Time::HiRes; BEGIN { use base qw(Exporter); @@ -84,9 +85,6 @@ use Storable qw( use pathhelp qw( exe_ext ); -use processhelp qw( - portable_sleep - ); use servers qw( checkcmd initserverconfig @@ -419,7 +417,7 @@ sub waitlockunlock { my $lockretry = $serverlogslocktimeout * 20; my @locks; while((@locks = logslocked()) && $lockretry--) { - portable_sleep(0.05); + Time::HiRes::sleep(0.05); } if(($lockretry < 0) && ($serverlogslocktimeout >= $defserverlogslocktimeout)) { @@ -1092,7 +1090,7 @@ sub singletest_clean { } } - portable_sleep($postcommanddelay) if($postcommanddelay); + Time::HiRes::sleep($postcommanddelay) if($postcommanddelay); my @killtestservers = getpart("client", "killserver"); if(@killtestservers) { diff --git a/tests/runtests.pl b/tests/runtests.pl index 407a37946..a470597bf 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -92,9 +92,6 @@ use pathhelp qw( exe_ext sys_native_current_path ); -use processhelp qw( - portable_sleep - ); use appveyor; use azure; diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm index fd762935a..1a3c99775 100644 --- a/tests/serverhelp.pm +++ b/tests/serverhelp.pm @@ -29,6 +29,8 @@ package serverhelp; use strict; use warnings; +use Time::HiRes; + BEGIN { use base qw(Exporter); @@ -52,13 +54,6 @@ BEGIN { datasockf_pidfilename datasockf_logfilename ); - - # sub second timestamping needs Time::HiRes - eval { - no warnings "all"; - require Time::HiRes; - import Time::HiRes qw( gettimeofday ); - } } use globalconfig; @@ -81,20 +76,10 @@ our $logfile; # server log file name, for logmsg # logmsg is general message logging subroutine for our test servers. # sub logmsg { - my $now; - # sub second timestamping needs Time::HiRes - if($Time::HiRes::VERSION) { - my ($seconds, $usec) = gettimeofday(); - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = - localtime($seconds); - $now = sprintf("%02d:%02d:%02d.%06d ", $hour, $min, $sec, $usec); - } - else { - my $seconds = time(); - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = - localtime($seconds); - $now = sprintf("%02d:%02d:%02d ", $hour, $min, $sec); - } + my ($seconds, $usec) = Time::HiRes::gettimeofday(); + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = + localtime($seconds); + my $now = sprintf("%02d:%02d:%02d.%06d ", $hour, $min, $sec, $usec); # we see warnings on Windows run that $logfile is used uninitialized # TODO: not found yet where this comes from $logfile = "serverhelp_uninitialized.log" if(!$logfile); diff --git a/tests/servers.pm b/tests/servers.pm index 02415c6ff..94809d3e7 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -380,7 +380,7 @@ sub startnew { logmsg "startnew: failed to write fake $pidfile with pid=$child\n"; } # could/should do a while connect fails sleep a bit and loop - portable_sleep($timeout); + Time::HiRes::sleep($timeout); if(checkdied($child)) { logmsg "startnew: child process has failed to start\n" if($verbose); return (-1,-1); |